use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::clients::RestClient;
use crate::rest::{ResourceError, ResourceOperation, ResourcePath, RestResource};
use crate::HttpMethod;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "snake_case")]
pub enum FulfillmentOrderStatus {
#[default]
Open,
InProgress,
Cancelled,
Incomplete,
Closed,
Scheduled,
OnHold,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "snake_case")]
pub enum FulfillmentOrderRequestStatus {
#[default]
Unsubmitted,
Submitted,
Accepted,
Rejected,
CancellationRequested,
CancellationAccepted,
CancellationRejected,
Closed,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum HoldReason {
AwaitingPayment,
HighRiskOfFraud,
IncorrectAddress,
InventoryOutOfStock,
Other,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct FulfillmentOrderLineItem {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shop_id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fulfillment_order_id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line_item_id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub inventory_item_id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub quantity: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fulfillable_quantity: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub variant_id: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct FulfillmentOrderDestination {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub first_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub company: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub address1: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub address2: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub city: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub province: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub country: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub zip: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub phone: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct FulfillmentHold {
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason_notes: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct DeliveryMethod {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub method_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_delivery_date_time: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_delivery_date_time: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct FulfillmentOrder {
#[serde(skip_serializing)]
pub id: Option<u64>,
#[serde(skip_serializing)]
pub order_id: Option<u64>,
#[serde(skip_serializing)]
pub shop_id: Option<u64>,
#[serde(skip_serializing)]
pub assigned_location_id: Option<u64>,
#[serde(skip_serializing)]
pub status: Option<FulfillmentOrderStatus>,
#[serde(skip_serializing)]
pub request_status: Option<FulfillmentOrderRequestStatus>,
#[serde(skip_serializing)]
pub supported_actions: Option<Vec<String>>,
#[serde(skip_serializing)]
pub destination: Option<FulfillmentOrderDestination>,
#[serde(skip_serializing)]
pub line_items: Option<Vec<FulfillmentOrderLineItem>>,
#[serde(skip_serializing)]
pub fulfill_at: Option<DateTime<Utc>>,
#[serde(skip_serializing)]
pub fulfill_by: Option<DateTime<Utc>>,
#[serde(skip_serializing)]
pub international_duties: Option<serde_json::Value>,
#[serde(skip_serializing)]
pub fulfillment_holds: Option<Vec<FulfillmentHold>>,
#[serde(skip_serializing)]
pub delivery_method: Option<DeliveryMethod>,
#[serde(skip_serializing)]
pub assigned_location: Option<serde_json::Value>,
#[serde(skip_serializing)]
pub merchant_requests: Option<Vec<serde_json::Value>>,
#[serde(skip_serializing)]
pub created_at: Option<DateTime<Utc>>,
#[serde(skip_serializing)]
pub updated_at: Option<DateTime<Utc>>,
#[serde(skip_serializing)]
pub admin_graphql_api_id: Option<String>,
}
impl RestResource for FulfillmentOrder {
type Id = u64;
type FindParams = FulfillmentOrderFindParams;
type AllParams = FulfillmentOrderListParams;
type CountParams = FulfillmentOrderCountParams;
const NAME: &'static str = "FulfillmentOrder";
const PLURAL: &'static str = "fulfillment_orders";
const PATHS: &'static [ResourcePath] = &[
ResourcePath::new(
HttpMethod::Get,
ResourceOperation::Find,
&["id"],
"fulfillment_orders/{id}",
),
ResourcePath::new(
HttpMethod::Get,
ResourceOperation::All,
&["order_id"],
"orders/{order_id}/fulfillment_orders",
),
];
fn get_id(&self) -> Option<Self::Id> {
self.id
}
}
impl FulfillmentOrder {
pub async fn cancel(&self, client: &RestClient) -> Result<Self, ResourceError> {
let id = self.get_id().ok_or(ResourceError::PathResolutionFailed {
resource: Self::NAME,
operation: "cancel",
})?;
let path = format!("fulfillment_orders/{id}/cancel");
let body = serde_json::json!({});
let response = client.post(&path, body, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
Self::NAME,
Some(&id.to_string()),
response.request_id(),
));
}
Self::parse_response(&response)
}
pub async fn close(
&self,
client: &RestClient,
message: Option<&str>,
) -> Result<Self, ResourceError> {
let id = self.get_id().ok_or(ResourceError::PathResolutionFailed {
resource: Self::NAME,
operation: "close",
})?;
let path = format!("fulfillment_orders/{id}/close");
let body = if let Some(msg) = message {
serde_json::json!({
"fulfillment_order": {
"message": msg
}
})
} else {
serde_json::json!({})
};
let response = client.post(&path, body, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
Self::NAME,
Some(&id.to_string()),
response.request_id(),
));
}
Self::parse_response(&response)
}
pub async fn hold(
&self,
client: &RestClient,
params: FulfillmentOrderHoldParams,
) -> Result<Self, ResourceError> {
let id = self.get_id().ok_or(ResourceError::PathResolutionFailed {
resource: Self::NAME,
operation: "hold",
})?;
let path = format!("fulfillment_orders/{id}/hold");
let body = serde_json::json!({
"fulfillment_hold": params
});
let response = client.post(&path, body, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
Self::NAME,
Some(&id.to_string()),
response.request_id(),
));
}
Self::parse_response(&response)
}
pub async fn move_location(
&self,
client: &RestClient,
params: FulfillmentOrderMoveParams,
) -> Result<Self, ResourceError> {
let id = self.get_id().ok_or(ResourceError::PathResolutionFailed {
resource: Self::NAME,
operation: "move",
})?;
let path = format!("fulfillment_orders/{id}/move");
let body = serde_json::json!({
"fulfillment_order": params
});
let response = client.post(&path, body, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
Self::NAME,
Some(&id.to_string()),
response.request_id(),
));
}
let response_body = &response.body;
if let Some(fo) = response_body.get("moved_fulfillment_order") {
return serde_json::from_value(fo.clone()).map_err(|e| {
ResourceError::Http(crate::clients::HttpError::Response(
crate::clients::HttpResponseError {
code: response.code,
message: format!("Failed to deserialize fulfillment_order: {e}"),
error_reference: response.request_id().map(ToString::to_string),
},
))
});
}
if let Some(fo) = response_body.get("original_fulfillment_order") {
return serde_json::from_value(fo.clone()).map_err(|e| {
ResourceError::Http(crate::clients::HttpError::Response(
crate::clients::HttpResponseError {
code: response.code,
message: format!("Failed to deserialize fulfillment_order: {e}"),
error_reference: response.request_id().map(ToString::to_string),
},
))
});
}
Self::parse_response(&response)
}
pub async fn open(&self, client: &RestClient) -> Result<Self, ResourceError> {
let id = self.get_id().ok_or(ResourceError::PathResolutionFailed {
resource: Self::NAME,
operation: "open",
})?;
let path = format!("fulfillment_orders/{id}/open");
let body = serde_json::json!({});
let response = client.post(&path, body, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
Self::NAME,
Some(&id.to_string()),
response.request_id(),
));
}
Self::parse_response(&response)
}
pub async fn release_hold(&self, client: &RestClient) -> Result<Self, ResourceError> {
let id = self.get_id().ok_or(ResourceError::PathResolutionFailed {
resource: Self::NAME,
operation: "release_hold",
})?;
let path = format!("fulfillment_orders/{id}/release_hold");
let body = serde_json::json!({});
let response = client.post(&path, body, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
Self::NAME,
Some(&id.to_string()),
response.request_id(),
));
}
Self::parse_response(&response)
}
pub async fn reschedule(
&self,
client: &RestClient,
params: FulfillmentOrderRescheduleParams,
) -> Result<Self, ResourceError> {
let id = self.get_id().ok_or(ResourceError::PathResolutionFailed {
resource: Self::NAME,
operation: "reschedule",
})?;
let path = format!("fulfillment_orders/{id}/reschedule");
let body = serde_json::json!(params);
let response = client.post(&path, body, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
Self::NAME,
Some(&id.to_string()),
response.request_id(),
));
}
Self::parse_response(&response)
}
fn parse_response(response: &crate::clients::HttpResponse) -> Result<Self, ResourceError> {
let fulfillment_order: Self = response
.body
.get("fulfillment_order")
.ok_or_else(|| {
ResourceError::Http(crate::clients::HttpError::Response(
crate::clients::HttpResponseError {
code: response.code,
message: "Missing 'fulfillment_order' in response".to_string(),
error_reference: response.request_id().map(ToString::to_string),
},
))
})
.and_then(|v| {
serde_json::from_value(v.clone()).map_err(|e| {
ResourceError::Http(crate::clients::HttpError::Response(
crate::clients::HttpResponseError {
code: response.code,
message: format!("Failed to deserialize fulfillment_order: {e}"),
error_reference: response.request_id().map(ToString::to_string),
},
))
})
})?;
Ok(fulfillment_order)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct FulfillmentOrderHoldParams {
pub reason: HoldReason,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason_notes: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub notify_merchant: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fulfillment_order_line_items: Option<Vec<FulfillmentOrderLineItemInput>>,
}
impl Default for HoldReason {
fn default() -> Self {
HoldReason::Other
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct FulfillmentOrderMoveParams {
pub new_location_id: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub fulfillment_order_line_items: Option<Vec<FulfillmentOrderLineItemInput>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct FulfillmentOrderRescheduleParams {
pub new_fulfill_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct FulfillmentOrderLineItemInput {
pub id: u64,
pub quantity: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct FulfillmentOrderFindParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub fields: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct FulfillmentOrderListParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fields: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_info: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct FulfillmentOrderCountParams {
}
#[derive(Debug, Clone, Default)]
pub struct FulfillmentRequest;
impl FulfillmentRequest {
pub async fn create(
client: &RestClient,
fulfillment_order_id: u64,
message: Option<&str>,
line_items: Option<Vec<FulfillmentOrderLineItemInput>>,
) -> Result<FulfillmentOrder, ResourceError> {
let path = format!("fulfillment_orders/{fulfillment_order_id}/fulfillment_request");
let mut request_body = serde_json::Map::new();
if let Some(msg) = message {
request_body.insert("message".to_string(), serde_json::json!(msg));
}
if let Some(items) = line_items {
request_body.insert(
"fulfillment_order_line_items".to_string(),
serde_json::json!(items),
);
}
let body = serde_json::json!({
"fulfillment_request": request_body
});
let response = client.post(&path, body, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
"FulfillmentRequest",
Some(&fulfillment_order_id.to_string()),
response.request_id(),
));
}
let fulfillment_order: FulfillmentOrder = response
.body
.get("original_fulfillment_order")
.or_else(|| response.body.get("fulfillment_order"))
.ok_or_else(|| {
ResourceError::Http(crate::clients::HttpError::Response(
crate::clients::HttpResponseError {
code: response.code,
message: "Missing fulfillment_order in response".to_string(),
error_reference: response.request_id().map(ToString::to_string),
},
))
})
.and_then(|v| {
serde_json::from_value(v.clone()).map_err(|e| {
ResourceError::Http(crate::clients::HttpError::Response(
crate::clients::HttpResponseError {
code: response.code,
message: format!("Failed to deserialize fulfillment_order: {e}"),
error_reference: response.request_id().map(ToString::to_string),
},
))
})
})?;
Ok(fulfillment_order)
}
pub async fn accept(
client: &RestClient,
fulfillment_order_id: u64,
message: Option<&str>,
) -> Result<FulfillmentOrder, ResourceError> {
let path = format!("fulfillment_orders/{fulfillment_order_id}/fulfillment_request/accept");
let body = if let Some(msg) = message {
serde_json::json!({
"fulfillment_request": {
"message": msg
}
})
} else {
serde_json::json!({})
};
let response = client.post(&path, body, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
"FulfillmentRequest",
Some(&fulfillment_order_id.to_string()),
response.request_id(),
));
}
FulfillmentOrder::parse_response(&response)
}
pub async fn reject(
client: &RestClient,
fulfillment_order_id: u64,
message: Option<&str>,
reason: Option<&str>,
) -> Result<FulfillmentOrder, ResourceError> {
let path = format!("fulfillment_orders/{fulfillment_order_id}/fulfillment_request/reject");
let mut request_body = serde_json::Map::new();
if let Some(msg) = message {
request_body.insert("message".to_string(), serde_json::json!(msg));
}
if let Some(r) = reason {
request_body.insert("reason".to_string(), serde_json::json!(r));
}
let body = if request_body.is_empty() {
serde_json::json!({})
} else {
serde_json::json!({
"fulfillment_request": request_body
})
};
let response = client.post(&path, body, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
"FulfillmentRequest",
Some(&fulfillment_order_id.to_string()),
response.request_id(),
));
}
FulfillmentOrder::parse_response(&response)
}
}
#[derive(Debug, Clone, Default)]
pub struct CancellationRequest;
impl CancellationRequest {
pub async fn create(
client: &RestClient,
fulfillment_order_id: u64,
message: Option<&str>,
) -> Result<FulfillmentOrder, ResourceError> {
let path = format!("fulfillment_orders/{fulfillment_order_id}/cancellation_request");
let body = if let Some(msg) = message {
serde_json::json!({
"cancellation_request": {
"message": msg
}
})
} else {
serde_json::json!({})
};
let response = client.post(&path, body, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
"CancellationRequest",
Some(&fulfillment_order_id.to_string()),
response.request_id(),
));
}
FulfillmentOrder::parse_response(&response)
}
pub async fn accept(
client: &RestClient,
fulfillment_order_id: u64,
message: Option<&str>,
) -> Result<FulfillmentOrder, ResourceError> {
let path =
format!("fulfillment_orders/{fulfillment_order_id}/cancellation_request/accept");
let body = if let Some(msg) = message {
serde_json::json!({
"cancellation_request": {
"message": msg
}
})
} else {
serde_json::json!({})
};
let response = client.post(&path, body, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
"CancellationRequest",
Some(&fulfillment_order_id.to_string()),
response.request_id(),
));
}
FulfillmentOrder::parse_response(&response)
}
pub async fn reject(
client: &RestClient,
fulfillment_order_id: u64,
message: Option<&str>,
) -> Result<FulfillmentOrder, ResourceError> {
let path =
format!("fulfillment_orders/{fulfillment_order_id}/cancellation_request/reject");
let body = if let Some(msg) = message {
serde_json::json!({
"cancellation_request": {
"message": msg
}
})
} else {
serde_json::json!({})
};
let response = client.post(&path, body, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
"CancellationRequest",
Some(&fulfillment_order_id.to_string()),
response.request_id(),
));
}
FulfillmentOrder::parse_response(&response)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::rest::{get_path, ResourceOperation};
#[test]
fn test_fulfillment_order_deserialization() {
let json_str = r##"{
"id": 1046000778,
"shop_id": 548380009,
"order_id": 450789469,
"assigned_location_id": 24826418,
"status": "open",
"request_status": "unsubmitted",
"supported_actions": ["create_fulfillment", "request_fulfillment"],
"destination": {
"id": 1046000779,
"first_name": "John",
"last_name": "Doe",
"address1": "123 Main St",
"city": "New York",
"province": "New York",
"country": "United States",
"zip": "10001",
"email": "john@example.com"
},
"line_items": [
{
"id": 1058737482,
"shop_id": 548380009,
"fulfillment_order_id": 1046000778,
"line_item_id": 669751112,
"inventory_item_id": 49148385,
"quantity": 1,
"fulfillable_quantity": 1
}
],
"fulfill_at": "2024-01-20T00:00:00Z",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z",
"admin_graphql_api_id": "gid://shopify/FulfillmentOrder/1046000778"
}"##;
let fo: FulfillmentOrder = serde_json::from_str(json_str).unwrap();
assert_eq!(fo.id, Some(1046000778));
assert_eq!(fo.shop_id, Some(548380009));
assert_eq!(fo.order_id, Some(450789469));
assert_eq!(fo.assigned_location_id, Some(24826418));
assert_eq!(fo.status, Some(FulfillmentOrderStatus::Open));
assert_eq!(
fo.request_status,
Some(FulfillmentOrderRequestStatus::Unsubmitted)
);
let dest = fo.destination.unwrap();
assert_eq!(dest.first_name.as_deref(), Some("John"));
assert_eq!(dest.city.as_deref(), Some("New York"));
let line_items = fo.line_items.unwrap();
assert_eq!(line_items.len(), 1);
assert_eq!(line_items[0].id, Some(1058737482));
assert_eq!(line_items[0].quantity, Some(1));
}
#[test]
fn test_fulfillment_order_status_enum_serialization() {
assert_eq!(
serde_json::to_string(&FulfillmentOrderStatus::Open).unwrap(),
"\"open\""
);
assert_eq!(
serde_json::to_string(&FulfillmentOrderStatus::InProgress).unwrap(),
"\"in_progress\""
);
assert_eq!(
serde_json::to_string(&FulfillmentOrderStatus::Cancelled).unwrap(),
"\"cancelled\""
);
assert_eq!(
serde_json::to_string(&FulfillmentOrderStatus::Incomplete).unwrap(),
"\"incomplete\""
);
assert_eq!(
serde_json::to_string(&FulfillmentOrderStatus::Closed).unwrap(),
"\"closed\""
);
assert_eq!(
serde_json::to_string(&FulfillmentOrderStatus::Scheduled).unwrap(),
"\"scheduled\""
);
assert_eq!(
serde_json::to_string(&FulfillmentOrderStatus::OnHold).unwrap(),
"\"on_hold\""
);
let open: FulfillmentOrderStatus = serde_json::from_str("\"open\"").unwrap();
let in_progress: FulfillmentOrderStatus = serde_json::from_str("\"in_progress\"").unwrap();
let on_hold: FulfillmentOrderStatus = serde_json::from_str("\"on_hold\"").unwrap();
assert_eq!(open, FulfillmentOrderStatus::Open);
assert_eq!(in_progress, FulfillmentOrderStatus::InProgress);
assert_eq!(on_hold, FulfillmentOrderStatus::OnHold);
assert_eq!(
FulfillmentOrderStatus::default(),
FulfillmentOrderStatus::Open
);
}
#[test]
fn test_fulfillment_order_request_status_enum_serialization() {
assert_eq!(
serde_json::to_string(&FulfillmentOrderRequestStatus::Unsubmitted).unwrap(),
"\"unsubmitted\""
);
assert_eq!(
serde_json::to_string(&FulfillmentOrderRequestStatus::Submitted).unwrap(),
"\"submitted\""
);
assert_eq!(
serde_json::to_string(&FulfillmentOrderRequestStatus::Accepted).unwrap(),
"\"accepted\""
);
assert_eq!(
serde_json::to_string(&FulfillmentOrderRequestStatus::Rejected).unwrap(),
"\"rejected\""
);
assert_eq!(
serde_json::to_string(&FulfillmentOrderRequestStatus::CancellationRequested).unwrap(),
"\"cancellation_requested\""
);
assert_eq!(
serde_json::to_string(&FulfillmentOrderRequestStatus::CancellationAccepted).unwrap(),
"\"cancellation_accepted\""
);
assert_eq!(
serde_json::to_string(&FulfillmentOrderRequestStatus::CancellationRejected).unwrap(),
"\"cancellation_rejected\""
);
let cancellation_requested: FulfillmentOrderRequestStatus =
serde_json::from_str("\"cancellation_requested\"").unwrap();
assert_eq!(
cancellation_requested,
FulfillmentOrderRequestStatus::CancellationRequested
);
}
#[test]
fn test_hold_reason_enum_serialization() {
assert_eq!(
serde_json::to_string(&HoldReason::AwaitingPayment).unwrap(),
"\"awaiting_payment\""
);
assert_eq!(
serde_json::to_string(&HoldReason::HighRiskOfFraud).unwrap(),
"\"high_risk_of_fraud\""
);
assert_eq!(
serde_json::to_string(&HoldReason::IncorrectAddress).unwrap(),
"\"incorrect_address\""
);
assert_eq!(
serde_json::to_string(&HoldReason::InventoryOutOfStock).unwrap(),
"\"inventory_out_of_stock\""
);
assert_eq!(
serde_json::to_string(&HoldReason::Other).unwrap(),
"\"other\""
);
let awaiting_payment: HoldReason = serde_json::from_str("\"awaiting_payment\"").unwrap();
assert_eq!(awaiting_payment, HoldReason::AwaitingPayment);
}
#[test]
fn test_fulfillment_order_paths() {
let find_path = get_path(FulfillmentOrder::PATHS, ResourceOperation::Find, &["id"]);
assert!(find_path.is_some());
assert_eq!(find_path.unwrap().template, "fulfillment_orders/{id}");
assert_eq!(find_path.unwrap().http_method, HttpMethod::Get);
let all_path = get_path(
FulfillmentOrder::PATHS,
ResourceOperation::All,
&["order_id"],
);
assert!(all_path.is_some());
assert_eq!(
all_path.unwrap().template,
"orders/{order_id}/fulfillment_orders"
);
let all_without_order = get_path(FulfillmentOrder::PATHS, ResourceOperation::All, &[]);
assert!(all_without_order.is_none());
let create_path = get_path(FulfillmentOrder::PATHS, ResourceOperation::Create, &[]);
assert!(create_path.is_none());
let update_path = get_path(FulfillmentOrder::PATHS, ResourceOperation::Update, &["id"]);
assert!(update_path.is_none());
let delete_path = get_path(FulfillmentOrder::PATHS, ResourceOperation::Delete, &["id"]);
assert!(delete_path.is_none());
let count_path = get_path(FulfillmentOrder::PATHS, ResourceOperation::Count, &[]);
assert!(count_path.is_none());
assert_eq!(FulfillmentOrder::NAME, "FulfillmentOrder");
assert_eq!(FulfillmentOrder::PLURAL, "fulfillment_orders");
}
#[test]
fn test_special_operation_method_signatures() {
fn _assert_cancel_signature<F, Fut>(f: F)
where
F: Fn(&FulfillmentOrder, &RestClient) -> Fut,
Fut: std::future::Future<Output = Result<FulfillmentOrder, ResourceError>>,
{
let _ = f;
}
fn _assert_hold_signature<F, Fut>(f: F)
where
F: Fn(&FulfillmentOrder, &RestClient, FulfillmentOrderHoldParams) -> Fut,
Fut: std::future::Future<Output = Result<FulfillmentOrder, ResourceError>>,
{
let _ = f;
}
fn _assert_move_signature<F, Fut>(f: F)
where
F: Fn(&FulfillmentOrder, &RestClient, FulfillmentOrderMoveParams) -> Fut,
Fut: std::future::Future<Output = Result<FulfillmentOrder, ResourceError>>,
{
let _ = f;
}
fn _assert_reschedule_signature<F, Fut>(f: F)
where
F: Fn(&FulfillmentOrder, &RestClient, FulfillmentOrderRescheduleParams) -> Fut,
Fut: std::future::Future<Output = Result<FulfillmentOrder, ResourceError>>,
{
let _ = f;
}
let hold_params = FulfillmentOrderHoldParams {
reason: HoldReason::AwaitingPayment,
reason_notes: Some("Waiting for wire transfer".to_string()),
notify_merchant: Some(true),
fulfillment_order_line_items: None,
};
let json = serde_json::to_value(&hold_params).unwrap();
assert_eq!(json["reason"], "awaiting_payment");
assert_eq!(json["reason_notes"], "Waiting for wire transfer");
assert_eq!(json["notify_merchant"], true);
let move_params = FulfillmentOrderMoveParams {
new_location_id: 12345,
fulfillment_order_line_items: Some(vec![FulfillmentOrderLineItemInput {
id: 111,
quantity: 2,
}]),
};
assert_eq!(move_params.new_location_id, 12345);
let reschedule_params = FulfillmentOrderRescheduleParams {
new_fulfill_at: DateTime::parse_from_rfc3339("2024-02-01T00:00:00Z")
.unwrap()
.with_timezone(&Utc),
};
assert!(reschedule_params.new_fulfill_at.timestamp() > 0);
}
#[test]
fn test_fulfillment_request_signatures() {
fn _assert_create_signature<F, Fut>(f: F)
where
F: Fn(
&RestClient,
u64,
Option<&str>,
Option<Vec<FulfillmentOrderLineItemInput>>,
) -> Fut,
Fut: std::future::Future<Output = Result<FulfillmentOrder, ResourceError>>,
{
let _ = f;
}
fn _assert_accept_signature<F, Fut>(f: F)
where
F: Fn(&RestClient, u64, Option<&str>) -> Fut,
Fut: std::future::Future<Output = Result<FulfillmentOrder, ResourceError>>,
{
let _ = f;
}
fn _assert_reject_signature<F, Fut>(f: F)
where
F: Fn(&RestClient, u64, Option<&str>, Option<&str>) -> Fut,
Fut: std::future::Future<Output = Result<FulfillmentOrder, ResourceError>>,
{
let _ = f;
}
}
#[test]
fn test_cancellation_request_signatures() {
fn _assert_create_signature<F, Fut>(f: F)
where
F: Fn(&RestClient, u64, Option<&str>) -> Fut,
Fut: std::future::Future<Output = Result<FulfillmentOrder, ResourceError>>,
{
let _ = f;
}
fn _assert_accept_signature<F, Fut>(f: F)
where
F: Fn(&RestClient, u64, Option<&str>) -> Fut,
Fut: std::future::Future<Output = Result<FulfillmentOrder, ResourceError>>,
{
let _ = f;
}
fn _assert_reject_signature<F, Fut>(f: F)
where
F: Fn(&RestClient, u64, Option<&str>) -> Fut,
Fut: std::future::Future<Output = Result<FulfillmentOrder, ResourceError>>,
{
let _ = f;
}
}
#[test]
fn test_fulfillment_order_get_id_returns_correct_value() {
let fo_with_id = FulfillmentOrder {
id: Some(1046000778),
order_id: Some(450789469),
..Default::default()
};
assert_eq!(fo_with_id.get_id(), Some(1046000778));
let fo_without_id = FulfillmentOrder {
id: None,
order_id: Some(450789469),
..Default::default()
};
assert_eq!(fo_without_id.get_id(), None);
}
}