Skip to main content

shopify_sdk/rest/resources/v2025_10/
order.rs

1//! Order resource implementation.
2//!
3//! This module provides the [`Order`] resource for managing orders in Shopify.
4//! Orders represent completed checkout transactions.
5//!
6//! # Resource-Specific Operations
7//!
8//! In addition to standard CRUD operations, the Order resource provides:
9//! - [`Order::cancel`] - Cancel an order
10//! - [`Order::close`] - Close an order
11//! - [`Order::open`] - Re-open a closed order
12//!
13//! # Example
14//!
15//! ```rust,ignore
16//! use shopify_sdk::rest::{RestResource, ResourceResponse};
17//! use shopify_sdk::rest::resources::v2025_10::{Order, OrderListParams, FinancialStatus};
18//!
19//! // Find a single order
20//! let order = Order::find(&client, 123, None).await?;
21//! println!("Order: {}", order.name.as_deref().unwrap_or(""));
22//!
23//! // List orders with filters
24//! let params = OrderListParams {
25//!     financial_status: Some(FinancialStatus::Paid),
26//!     limit: Some(50),
27//!     ..Default::default()
28//! };
29//! let orders = Order::all(&client, Some(params)).await?;
30//!
31//! // Cancel an order
32//! let cancelled_order = order.cancel(&client).await?;
33//!
34//! // Close an order
35//! let closed_order = order.close(&client).await?;
36//!
37//! // Re-open a closed order
38//! let reopened_order = closed_order.open(&client).await?;
39//! ```
40
41use chrono::{DateTime, Utc};
42use serde::{Deserialize, Serialize};
43
44use crate::clients::RestClient;
45use crate::rest::{ResourceError, ResourceOperation, ResourcePath, RestResource};
46use crate::HttpMethod;
47
48use super::common::{Address, DiscountApplication, LineItem, NoteAttribute, ShippingLine, TaxLine};
49use super::customer::Customer;
50
51/// The financial status of an order.
52///
53/// Indicates the payment status of the order.
54#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
55#[serde(rename_all = "snake_case")]
56pub enum FinancialStatus {
57    /// Payment is pending.
58    #[default]
59    Pending,
60    /// Payment has been authorized but not captured.
61    Authorized,
62    /// Payment has been partially paid.
63    PartiallyPaid,
64    /// Payment has been fully captured.
65    Paid,
66    /// Payment has been partially refunded.
67    PartiallyRefunded,
68    /// Payment has been fully refunded.
69    Refunded,
70    /// Payment authorization has been voided.
71    Voided,
72}
73
74/// The fulfillment status of an order.
75///
76/// Indicates the shipping/fulfillment status of the order.
77#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
78#[serde(rename_all = "snake_case")]
79pub enum FulfillmentStatus {
80    /// All line items have been fulfilled.
81    Fulfilled,
82    /// Some line items have been fulfilled.
83    Partial,
84    /// No line items have been fulfilled.
85    Unfulfilled,
86    /// Items have been restocked.
87    Restocked,
88}
89
90/// The reason for canceling an order.
91#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
92#[serde(rename_all = "snake_case")]
93pub enum CancelReason {
94    /// Customer requested cancellation.
95    Customer,
96    /// Order was identified as fraudulent.
97    Fraud,
98    /// Items were out of stock.
99    Inventory,
100    /// Payment was declined.
101    Declined,
102    /// Other reason for cancellation.
103    Other,
104}
105
106/// A discount code applied to an order.
107///
108/// Different from `DiscountApplication`, this represents the actual code
109/// that was entered at checkout.
110#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
111pub struct DiscountCode {
112    /// The discount code that was entered.
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub code: Option<String>,
115
116    /// The amount of the discount.
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub amount: Option<String>,
119
120    /// The type of discount (e.g., "percentage", `"fixed_amount"`).
121    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
122    pub discount_type: Option<String>,
123}
124
125/// A refund associated with an order.
126///
127/// Contains information about refunded amounts, line items, and transactions.
128#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
129pub struct Refund {
130    /// The unique identifier of the refund.
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub id: Option<u64>,
133
134    /// The ID of the order this refund belongs to.
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub order_id: Option<u64>,
137
138    /// When the refund was created.
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub created_at: Option<DateTime<Utc>>,
141
142    /// An optional note attached to the refund.
143    #[serde(skip_serializing_if = "Option::is_none")]
144    pub note: Option<String>,
145
146    /// The ID of the user who processed the refund.
147    #[serde(skip_serializing_if = "Option::is_none")]
148    pub user_id: Option<u64>,
149
150    /// When the refund was processed.
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub processed_at: Option<DateTime<Utc>>,
153
154    /// Whether items should be restocked.
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub restock: Option<bool>,
157
158    /// Duties associated with the refund (complex structure).
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub duties: Option<serde_json::Value>,
161
162    /// Refunded duties (complex structure).
163    #[serde(skip_serializing_if = "Option::is_none")]
164    pub refund_duties: Option<serde_json::Value>,
165
166    /// Line items included in the refund (complex structure).
167    #[serde(skip_serializing_if = "Option::is_none")]
168    pub refund_line_items: Option<serde_json::Value>,
169
170    /// Transactions for the refund (complex structure).
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub transactions: Option<serde_json::Value>,
173
174    /// Order adjustments from the refund (complex structure).
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pub order_adjustments: Option<serde_json::Value>,
177
178    /// The admin GraphQL API ID.
179    #[serde(skip_serializing_if = "Option::is_none")]
180    pub admin_graphql_api_id: Option<String>,
181}
182
183/// An embedded fulfillment within an order response.
184///
185/// This is a simplified view of fulfillment data when embedded in order responses.
186#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
187pub struct OrderFulfillment {
188    /// The unique identifier of the fulfillment.
189    #[serde(skip_serializing_if = "Option::is_none")]
190    pub id: Option<u64>,
191
192    /// The ID of the order this fulfillment belongs to.
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub order_id: Option<u64>,
195
196    /// The status of the fulfillment.
197    #[serde(skip_serializing_if = "Option::is_none")]
198    pub status: Option<String>,
199
200    /// When the fulfillment was created.
201    #[serde(skip_serializing_if = "Option::is_none")]
202    pub created_at: Option<DateTime<Utc>>,
203
204    /// The fulfillment service.
205    #[serde(skip_serializing_if = "Option::is_none")]
206    pub service: Option<String>,
207
208    /// When the fulfillment was last updated.
209    #[serde(skip_serializing_if = "Option::is_none")]
210    pub updated_at: Option<DateTime<Utc>>,
211
212    /// The tracking company name.
213    #[serde(skip_serializing_if = "Option::is_none")]
214    pub tracking_company: Option<String>,
215
216    /// The shipment status.
217    #[serde(skip_serializing_if = "Option::is_none")]
218    pub shipment_status: Option<String>,
219
220    /// The ID of the location that fulfilled the order.
221    #[serde(skip_serializing_if = "Option::is_none")]
222    pub location_id: Option<u64>,
223
224    /// The tracking number.
225    #[serde(skip_serializing_if = "Option::is_none")]
226    pub tracking_number: Option<String>,
227
228    /// Multiple tracking numbers (if any).
229    #[serde(skip_serializing_if = "Option::is_none")]
230    pub tracking_numbers: Option<Vec<String>>,
231
232    /// The tracking URL.
233    #[serde(skip_serializing_if = "Option::is_none")]
234    pub tracking_url: Option<String>,
235
236    /// Multiple tracking URLs (if any).
237    #[serde(skip_serializing_if = "Option::is_none")]
238    pub tracking_urls: Option<Vec<String>>,
239
240    /// Line items included in this fulfillment (complex structure).
241    #[serde(skip_serializing_if = "Option::is_none")]
242    pub line_items: Option<serde_json::Value>,
243
244    /// The admin GraphQL API ID.
245    #[serde(skip_serializing_if = "Option::is_none")]
246    pub admin_graphql_api_id: Option<String>,
247}
248
249/// An order in Shopify.
250///
251/// Orders represent completed checkout transactions including the line items,
252/// shipping information, and payment status.
253///
254/// # Read-Only Fields
255///
256/// The following fields are read-only and will not be sent in create/update requests:
257/// - `id`, `name`, `order_number`
258/// - `created_at`, `updated_at`
259/// - `confirmation_number`, `admin_graphql_api_id`
260///
261/// # Example
262///
263/// ```rust,ignore
264/// use shopify_sdk::rest::resources::v2025_10::Order;
265///
266/// let order = Order {
267///     email: Some("customer@example.com".to_string()),
268///     note: Some("Please gift wrap".to_string()),
269///     ..Default::default()
270/// };
271/// ```
272#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
273pub struct Order {
274    // --- Read-only fields (not serialized) ---
275    /// The unique identifier of the order.
276    #[serde(skip_serializing)]
277    pub id: Option<u64>,
278
279    /// The order name (e.g., "#1001").
280    #[serde(skip_serializing)]
281    pub name: Option<String>,
282
283    /// The order number (integer portion of name).
284    #[serde(skip_serializing)]
285    pub order_number: Option<u64>,
286
287    /// When the order was created.
288    #[serde(skip_serializing)]
289    pub created_at: Option<DateTime<Utc>>,
290
291    /// When the order was last updated.
292    #[serde(skip_serializing)]
293    pub updated_at: Option<DateTime<Utc>>,
294
295    /// The confirmation number.
296    #[serde(skip_serializing)]
297    pub confirmation_number: Option<String>,
298
299    /// The admin GraphQL API ID.
300    #[serde(skip_serializing)]
301    pub admin_graphql_api_id: Option<String>,
302
303    // --- Core fields ---
304    /// The customer's email address.
305    #[serde(skip_serializing_if = "Option::is_none")]
306    pub email: Option<String>,
307
308    /// The customer's phone number.
309    #[serde(skip_serializing_if = "Option::is_none")]
310    pub phone: Option<String>,
311
312    /// When the order was closed.
313    #[serde(skip_serializing_if = "Option::is_none")]
314    pub closed_at: Option<DateTime<Utc>>,
315
316    /// When the order was cancelled.
317    #[serde(skip_serializing_if = "Option::is_none")]
318    pub cancelled_at: Option<DateTime<Utc>>,
319
320    /// The reason the order was cancelled.
321    #[serde(skip_serializing_if = "Option::is_none")]
322    pub cancel_reason: Option<CancelReason>,
323
324    /// The financial status of the order.
325    #[serde(skip_serializing_if = "Option::is_none")]
326    pub financial_status: Option<FinancialStatus>,
327
328    /// The fulfillment status of the order.
329    #[serde(skip_serializing_if = "Option::is_none")]
330    pub fulfillment_status: Option<FulfillmentStatus>,
331
332    /// The currency code (e.g., "USD").
333    #[serde(skip_serializing_if = "Option::is_none")]
334    pub currency: Option<String>,
335
336    /// The total price of the order including taxes and discounts.
337    #[serde(skip_serializing_if = "Option::is_none")]
338    pub total_price: Option<String>,
339
340    /// The subtotal price (before taxes and shipping).
341    #[serde(skip_serializing_if = "Option::is_none")]
342    pub subtotal_price: Option<String>,
343
344    /// The total tax amount.
345    #[serde(skip_serializing_if = "Option::is_none")]
346    pub total_tax: Option<String>,
347
348    /// The total discount amount.
349    #[serde(skip_serializing_if = "Option::is_none")]
350    pub total_discounts: Option<String>,
351
352    /// The total weight in grams.
353    #[serde(skip_serializing_if = "Option::is_none")]
354    pub total_weight: Option<i64>,
355
356    /// Whether taxes are included in the prices.
357    #[serde(skip_serializing_if = "Option::is_none")]
358    pub taxes_included: Option<bool>,
359
360    /// Whether the customer has opted into marketing.
361    #[serde(skip_serializing_if = "Option::is_none")]
362    pub buyer_accepts_marketing: Option<bool>,
363
364    /// An optional note attached to the order.
365    #[serde(skip_serializing_if = "Option::is_none")]
366    pub note: Option<String>,
367
368    /// Custom note attributes (key-value pairs).
369    #[serde(skip_serializing_if = "Option::is_none")]
370    pub note_attributes: Option<Vec<NoteAttribute>>,
371
372    /// Comma-separated tags.
373    #[serde(skip_serializing_if = "Option::is_none")]
374    pub tags: Option<String>,
375
376    /// The ID of the app that created the order.
377    #[serde(skip_serializing_if = "Option::is_none")]
378    pub app_id: Option<u64>,
379
380    /// The IP address of the browser used to place the order.
381    #[serde(skip_serializing_if = "Option::is_none")]
382    pub browser_ip: Option<String>,
383
384    /// The three-letter language code of the customer.
385    #[serde(skip_serializing_if = "Option::is_none")]
386    pub customer_locale: Option<String>,
387
388    /// The URL for the order status page.
389    #[serde(skip_serializing_if = "Option::is_none")]
390    pub order_status_url: Option<String>,
391
392    /// When the order was processed.
393    #[serde(skip_serializing_if = "Option::is_none")]
394    pub processed_at: Option<DateTime<Utc>>,
395
396    /// The source name (e.g., "web", "pos").
397    #[serde(skip_serializing_if = "Option::is_none")]
398    pub source_name: Option<String>,
399
400    /// The total price in shop currency.
401    #[serde(skip_serializing_if = "Option::is_none")]
402    pub total_price_usd: Option<String>,
403
404    /// Total shipping price.
405    #[serde(skip_serializing_if = "Option::is_none")]
406    pub total_shipping_price_set: Option<serde_json::Value>,
407
408    /// Total line items price.
409    #[serde(skip_serializing_if = "Option::is_none")]
410    pub total_line_items_price: Option<String>,
411
412    /// Total outstanding amount.
413    #[serde(skip_serializing_if = "Option::is_none")]
414    pub total_outstanding: Option<String>,
415
416    /// Current total price.
417    #[serde(skip_serializing_if = "Option::is_none")]
418    pub current_total_price: Option<String>,
419
420    /// Current subtotal price.
421    #[serde(skip_serializing_if = "Option::is_none")]
422    pub current_subtotal_price: Option<String>,
423
424    /// Current total tax.
425    #[serde(skip_serializing_if = "Option::is_none")]
426    pub current_total_tax: Option<String>,
427
428    /// Current total discounts.
429    #[serde(skip_serializing_if = "Option::is_none")]
430    pub current_total_discounts: Option<String>,
431
432    /// Whether the order has been confirmed.
433    #[serde(skip_serializing_if = "Option::is_none")]
434    pub confirmed: Option<bool>,
435
436    /// Whether the order is a test order.
437    #[serde(skip_serializing_if = "Option::is_none")]
438    pub test: Option<bool>,
439
440    /// The ID of the user who placed the order.
441    #[serde(skip_serializing_if = "Option::is_none")]
442    pub user_id: Option<u64>,
443
444    /// The ID of the location.
445    #[serde(skip_serializing_if = "Option::is_none")]
446    pub location_id: Option<u64>,
447
448    /// The source identifier.
449    #[serde(skip_serializing_if = "Option::is_none")]
450    pub source_identifier: Option<String>,
451
452    /// The source URL.
453    #[serde(skip_serializing_if = "Option::is_none")]
454    pub source_url: Option<String>,
455
456    /// The device ID.
457    #[serde(skip_serializing_if = "Option::is_none")]
458    pub device_id: Option<u64>,
459
460    /// The landing site.
461    #[serde(skip_serializing_if = "Option::is_none")]
462    pub landing_site: Option<String>,
463
464    /// The referring site.
465    #[serde(skip_serializing_if = "Option::is_none")]
466    pub referring_site: Option<String>,
467
468    /// The gateway.
469    #[serde(skip_serializing_if = "Option::is_none")]
470    pub gateway: Option<String>,
471
472    /// Payment gateway names.
473    #[serde(skip_serializing_if = "Option::is_none")]
474    pub payment_gateway_names: Option<Vec<String>>,
475
476    /// Processing method.
477    #[serde(skip_serializing_if = "Option::is_none")]
478    pub processing_method: Option<String>,
479
480    /// Reference for external order.
481    #[serde(skip_serializing_if = "Option::is_none")]
482    pub reference: Option<String>,
483
484    /// Checkout ID.
485    #[serde(skip_serializing_if = "Option::is_none")]
486    pub checkout_id: Option<u64>,
487
488    /// Checkout token.
489    #[serde(skip_serializing_if = "Option::is_none")]
490    pub checkout_token: Option<String>,
491
492    /// Cart token.
493    #[serde(skip_serializing_if = "Option::is_none")]
494    pub cart_token: Option<String>,
495
496    /// Token for the order.
497    #[serde(skip_serializing_if = "Option::is_none")]
498    pub token: Option<String>,
499
500    // --- Nested structures ---
501    /// The line items in the order.
502    #[serde(skip_serializing_if = "Option::is_none")]
503    pub line_items: Option<Vec<LineItem>>,
504
505    /// The billing address.
506    #[serde(skip_serializing_if = "Option::is_none")]
507    pub billing_address: Option<Address>,
508
509    /// The shipping address.
510    #[serde(skip_serializing_if = "Option::is_none")]
511    pub shipping_address: Option<Address>,
512
513    /// Tax lines for the order.
514    #[serde(skip_serializing_if = "Option::is_none")]
515    pub tax_lines: Option<Vec<TaxLine>>,
516
517    /// Discount codes applied to the order.
518    #[serde(skip_serializing_if = "Option::is_none")]
519    pub discount_codes: Option<Vec<DiscountCode>>,
520
521    /// Discount applications on the order.
522    #[serde(skip_serializing_if = "Option::is_none")]
523    pub discount_applications: Option<Vec<DiscountApplication>>,
524
525    /// Shipping lines for the order.
526    #[serde(skip_serializing_if = "Option::is_none")]
527    pub shipping_lines: Option<Vec<ShippingLine>>,
528
529    /// Fulfillments for the order (when embedded).
530    #[serde(skip_serializing_if = "Option::is_none")]
531    pub fulfillments: Option<Vec<OrderFulfillment>>,
532
533    /// Refunds for the order.
534    #[serde(skip_serializing_if = "Option::is_none")]
535    pub refunds: Option<Vec<Refund>>,
536
537    /// The customer who placed the order.
538    #[serde(skip_serializing_if = "Option::is_none")]
539    pub customer: Option<Customer>,
540
541    /// Client details (complex structure).
542    #[serde(skip_serializing_if = "Option::is_none")]
543    pub client_details: Option<serde_json::Value>,
544
545    /// Payment details (complex structure).
546    #[serde(skip_serializing_if = "Option::is_none")]
547    pub payment_details: Option<serde_json::Value>,
548}
549
550impl RestResource for Order {
551    type Id = u64;
552    type FindParams = OrderFindParams;
553    type AllParams = OrderListParams;
554    type CountParams = OrderCountParams;
555
556    const NAME: &'static str = "Order";
557    const PLURAL: &'static str = "orders";
558
559    const PATHS: &'static [ResourcePath] = &[
560        ResourcePath::new(
561            HttpMethod::Get,
562            ResourceOperation::Find,
563            &["id"],
564            "orders/{id}",
565        ),
566        ResourcePath::new(HttpMethod::Get, ResourceOperation::All, &[], "orders"),
567        ResourcePath::new(
568            HttpMethod::Get,
569            ResourceOperation::Count,
570            &[],
571            "orders/count",
572        ),
573        ResourcePath::new(HttpMethod::Post, ResourceOperation::Create, &[], "orders"),
574        ResourcePath::new(
575            HttpMethod::Put,
576            ResourceOperation::Update,
577            &["id"],
578            "orders/{id}",
579        ),
580        ResourcePath::new(
581            HttpMethod::Delete,
582            ResourceOperation::Delete,
583            &["id"],
584            "orders/{id}",
585        ),
586    ];
587
588    fn get_id(&self) -> Option<Self::Id> {
589        self.id
590    }
591}
592
593impl Order {
594    /// Cancels the order.
595    ///
596    /// Sends a POST request to `/admin/api/{version}/orders/{id}/cancel.json`.
597    ///
598    /// # Arguments
599    ///
600    /// * `client` - The REST client to use for the request
601    ///
602    /// # Errors
603    ///
604    /// Returns [`ResourceError::NotFound`] if the order doesn't exist.
605    /// Returns [`ResourceError::PathResolutionFailed`] if the order has no ID.
606    ///
607    /// # Example
608    ///
609    /// ```rust,ignore
610    /// let order = Order::find(&client, 123, None).await?;
611    /// let cancelled = order.cancel(&client).await?;
612    /// assert!(cancelled.cancelled_at.is_some());
613    /// ```
614    pub async fn cancel(&self, client: &RestClient) -> Result<Self, ResourceError> {
615        let id = self.get_id().ok_or(ResourceError::PathResolutionFailed {
616            resource: Self::NAME,
617            operation: "cancel",
618        })?;
619
620        let path = format!("orders/{id}/cancel");
621        let body = serde_json::json!({});
622
623        let response = client.post(&path, body, None).await?;
624
625        if !response.is_ok() {
626            return Err(ResourceError::from_http_response(
627                response.code,
628                &response.body,
629                Self::NAME,
630                Some(&id.to_string()),
631                response.request_id(),
632            ));
633        }
634
635        // Parse the response - Shopify returns the order wrapped in "order" key
636        let order: Self = response
637            .body
638            .get("order")
639            .ok_or_else(|| {
640                ResourceError::Http(crate::clients::HttpError::Response(
641                    crate::clients::HttpResponseError {
642                        code: response.code,
643                        message: "Missing 'order' in response".to_string(),
644                        error_reference: response.request_id().map(ToString::to_string),
645                    },
646                ))
647            })
648            .and_then(|v| {
649                serde_json::from_value(v.clone()).map_err(|e| {
650                    ResourceError::Http(crate::clients::HttpError::Response(
651                        crate::clients::HttpResponseError {
652                            code: response.code,
653                            message: format!("Failed to deserialize order: {e}"),
654                            error_reference: response.request_id().map(ToString::to_string),
655                        },
656                    ))
657                })
658            })?;
659
660        Ok(order)
661    }
662
663    /// Closes the order.
664    ///
665    /// Sends a POST request to `/admin/api/{version}/orders/{id}/close.json`.
666    ///
667    /// # Arguments
668    ///
669    /// * `client` - The REST client to use for the request
670    ///
671    /// # Errors
672    ///
673    /// Returns [`ResourceError::NotFound`] if the order doesn't exist.
674    /// Returns [`ResourceError::PathResolutionFailed`] if the order has no ID.
675    ///
676    /// # Example
677    ///
678    /// ```rust,ignore
679    /// let order = Order::find(&client, 123, None).await?;
680    /// let closed = order.close(&client).await?;
681    /// assert!(closed.closed_at.is_some());
682    /// ```
683    pub async fn close(&self, client: &RestClient) -> Result<Self, ResourceError> {
684        let id = self.get_id().ok_or(ResourceError::PathResolutionFailed {
685            resource: Self::NAME,
686            operation: "close",
687        })?;
688
689        let path = format!("orders/{id}/close");
690        let body = serde_json::json!({});
691
692        let response = client.post(&path, body, None).await?;
693
694        if !response.is_ok() {
695            return Err(ResourceError::from_http_response(
696                response.code,
697                &response.body,
698                Self::NAME,
699                Some(&id.to_string()),
700                response.request_id(),
701            ));
702        }
703
704        // Parse the response - Shopify returns the order wrapped in "order" key
705        let order: Self = response
706            .body
707            .get("order")
708            .ok_or_else(|| {
709                ResourceError::Http(crate::clients::HttpError::Response(
710                    crate::clients::HttpResponseError {
711                        code: response.code,
712                        message: "Missing 'order' in response".to_string(),
713                        error_reference: response.request_id().map(ToString::to_string),
714                    },
715                ))
716            })
717            .and_then(|v| {
718                serde_json::from_value(v.clone()).map_err(|e| {
719                    ResourceError::Http(crate::clients::HttpError::Response(
720                        crate::clients::HttpResponseError {
721                            code: response.code,
722                            message: format!("Failed to deserialize order: {e}"),
723                            error_reference: response.request_id().map(ToString::to_string),
724                        },
725                    ))
726                })
727            })?;
728
729        Ok(order)
730    }
731
732    /// Re-opens a closed order.
733    ///
734    /// Sends a POST request to `/admin/api/{version}/orders/{id}/open.json`.
735    ///
736    /// # Arguments
737    ///
738    /// * `client` - The REST client to use for the request
739    ///
740    /// # Errors
741    ///
742    /// Returns [`ResourceError::NotFound`] if the order doesn't exist.
743    /// Returns [`ResourceError::PathResolutionFailed`] if the order has no ID.
744    ///
745    /// # Example
746    ///
747    /// ```rust,ignore
748    /// let order = Order::find(&client, 123, None).await?;
749    /// let reopened = order.open(&client).await?;
750    /// assert!(reopened.closed_at.is_none());
751    /// ```
752    pub async fn open(&self, client: &RestClient) -> Result<Self, ResourceError> {
753        let id = self.get_id().ok_or(ResourceError::PathResolutionFailed {
754            resource: Self::NAME,
755            operation: "open",
756        })?;
757
758        let path = format!("orders/{id}/open");
759        let body = serde_json::json!({});
760
761        let response = client.post(&path, body, None).await?;
762
763        if !response.is_ok() {
764            return Err(ResourceError::from_http_response(
765                response.code,
766                &response.body,
767                Self::NAME,
768                Some(&id.to_string()),
769                response.request_id(),
770            ));
771        }
772
773        // Parse the response - Shopify returns the order wrapped in "order" key
774        let order: Self = response
775            .body
776            .get("order")
777            .ok_or_else(|| {
778                ResourceError::Http(crate::clients::HttpError::Response(
779                    crate::clients::HttpResponseError {
780                        code: response.code,
781                        message: "Missing 'order' in response".to_string(),
782                        error_reference: response.request_id().map(ToString::to_string),
783                    },
784                ))
785            })
786            .and_then(|v| {
787                serde_json::from_value(v.clone()).map_err(|e| {
788                    ResourceError::Http(crate::clients::HttpError::Response(
789                        crate::clients::HttpResponseError {
790                            code: response.code,
791                            message: format!("Failed to deserialize order: {e}"),
792                            error_reference: response.request_id().map(ToString::to_string),
793                        },
794                    ))
795                })
796            })?;
797
798        Ok(order)
799    }
800}
801
802/// Parameters for finding a single order.
803#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
804pub struct OrderFindParams {
805    /// Comma-separated list of fields to include in the response.
806    #[serde(skip_serializing_if = "Option::is_none")]
807    pub fields: Option<String>,
808}
809
810/// Parameters for listing orders.
811///
812/// All fields are optional. Unset fields will not be included in the request.
813#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
814pub struct OrderListParams {
815    /// Comma-separated list of order IDs to retrieve.
816    #[serde(skip_serializing_if = "Option::is_none")]
817    pub ids: Option<Vec<u64>>,
818
819    /// Maximum number of results to return.
820    #[serde(skip_serializing_if = "Option::is_none")]
821    pub limit: Option<u32>,
822
823    /// Return only orders after the specified ID.
824    #[serde(skip_serializing_if = "Option::is_none")]
825    pub since_id: Option<u64>,
826
827    /// Filter by order status ("open", "closed", "cancelled", "any").
828    #[serde(skip_serializing_if = "Option::is_none")]
829    pub status: Option<String>,
830
831    /// Filter by financial status.
832    #[serde(skip_serializing_if = "Option::is_none")]
833    pub financial_status: Option<FinancialStatus>,
834
835    /// Filter by fulfillment status.
836    #[serde(skip_serializing_if = "Option::is_none")]
837    pub fulfillment_status: Option<FulfillmentStatus>,
838
839    /// Show orders created at or after this date.
840    #[serde(skip_serializing_if = "Option::is_none")]
841    pub created_at_min: Option<DateTime<Utc>>,
842
843    /// Show orders created at or before this date.
844    #[serde(skip_serializing_if = "Option::is_none")]
845    pub created_at_max: Option<DateTime<Utc>>,
846
847    /// Show orders last updated at or after this date.
848    #[serde(skip_serializing_if = "Option::is_none")]
849    pub updated_at_min: Option<DateTime<Utc>>,
850
851    /// Show orders last updated at or before this date.
852    #[serde(skip_serializing_if = "Option::is_none")]
853    pub updated_at_max: Option<DateTime<Utc>>,
854
855    /// Show orders processed at or after this date.
856    #[serde(skip_serializing_if = "Option::is_none")]
857    pub processed_at_min: Option<DateTime<Utc>>,
858
859    /// Show orders processed at or before this date.
860    #[serde(skip_serializing_if = "Option::is_none")]
861    pub processed_at_max: Option<DateTime<Utc>>,
862
863    /// Filter by the app that created the order.
864    #[serde(skip_serializing_if = "Option::is_none")]
865    pub attribution_app_id: Option<u64>,
866
867    /// Comma-separated list of fields to include in the response.
868    #[serde(skip_serializing_if = "Option::is_none")]
869    pub fields: Option<String>,
870
871    /// Page info for cursor-based pagination.
872    #[serde(skip_serializing_if = "Option::is_none")]
873    pub page_info: Option<String>,
874}
875
876/// Parameters for counting orders.
877#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
878pub struct OrderCountParams {
879    /// Filter by order status.
880    #[serde(skip_serializing_if = "Option::is_none")]
881    pub status: Option<String>,
882
883    /// Filter by financial status.
884    #[serde(skip_serializing_if = "Option::is_none")]
885    pub financial_status: Option<FinancialStatus>,
886
887    /// Filter by fulfillment status.
888    #[serde(skip_serializing_if = "Option::is_none")]
889    pub fulfillment_status: Option<FulfillmentStatus>,
890
891    /// Show orders created at or after this date.
892    #[serde(skip_serializing_if = "Option::is_none")]
893    pub created_at_min: Option<DateTime<Utc>>,
894
895    /// Show orders created at or before this date.
896    #[serde(skip_serializing_if = "Option::is_none")]
897    pub created_at_max: Option<DateTime<Utc>>,
898
899    /// Show orders last updated at or after this date.
900    #[serde(skip_serializing_if = "Option::is_none")]
901    pub updated_at_min: Option<DateTime<Utc>>,
902
903    /// Show orders last updated at or before this date.
904    #[serde(skip_serializing_if = "Option::is_none")]
905    pub updated_at_max: Option<DateTime<Utc>>,
906}
907
908#[cfg(test)]
909mod tests {
910    use super::*;
911    use crate::rest::{get_path, ResourceOperation};
912
913    #[test]
914    fn test_order_struct_serialization() {
915        let order = Order {
916            id: Some(450789469),
917            name: Some("#1001".to_string()),
918            email: Some("customer@example.com".to_string()),
919            phone: Some("+1-555-555-5555".to_string()),
920            total_price: Some("199.99".to_string()),
921            subtotal_price: Some("179.99".to_string()),
922            total_tax: Some("15.00".to_string()),
923            total_discounts: Some("5.00".to_string()),
924            currency: Some("USD".to_string()),
925            financial_status: Some(FinancialStatus::Paid),
926            fulfillment_status: Some(FulfillmentStatus::Unfulfilled),
927            tags: Some("important, vip".to_string()),
928            note: Some("Please gift wrap".to_string()),
929            buyer_accepts_marketing: Some(true),
930            ..Default::default()
931        };
932
933        let json = serde_json::to_string(&order).unwrap();
934        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
935
936        // Writable fields should be present
937        assert_eq!(parsed["email"], "customer@example.com");
938        assert_eq!(parsed["phone"], "+1-555-555-5555");
939        assert_eq!(parsed["total_price"], "199.99");
940        assert_eq!(parsed["currency"], "USD");
941        assert_eq!(parsed["financial_status"], "paid");
942        assert_eq!(parsed["fulfillment_status"], "unfulfilled");
943        assert_eq!(parsed["note"], "Please gift wrap");
944
945        // Read-only fields should NOT be serialized
946        assert!(parsed.get("id").is_none());
947        assert!(parsed.get("name").is_none());
948    }
949
950    #[test]
951    fn test_order_deserialization_with_nested_line_items() {
952        // Use r##"..."## to allow # characters in the JSON string
953        let json_str = r##"{
954            "id": 450789469,
955            "name": "#1001",
956            "email": "customer@example.com",
957            "order_number": 1001,
958            "total_price": "199.99",
959            "financial_status": "paid",
960            "fulfillment_status": "partial",
961            "line_items": [
962                {
963                    "id": 669751112,
964                    "variant_id": 457924702,
965                    "product_id": 632910392,
966                    "title": "IPod Nano - 8GB",
967                    "quantity": 1,
968                    "price": "199.00",
969                    "sku": "IPOD2008BLACK",
970                    "taxable": true,
971                    "tax_lines": [
972                        {
973                            "title": "State Tax",
974                            "price": "15.99",
975                            "rate": 0.08
976                        }
977                    ]
978                }
979            ],
980            "billing_address": {
981                "first_name": "John",
982                "last_name": "Doe",
983                "address1": "123 Main St",
984                "city": "New York",
985                "province": "New York",
986                "country": "United States",
987                "zip": "10001"
988            },
989            "customer": {
990                "id": 207119551,
991                "email": "customer@example.com",
992                "first_name": "John",
993                "last_name": "Doe"
994            }
995        }"##;
996
997        let order: Order = serde_json::from_str(json_str).unwrap();
998
999        assert_eq!(order.id, Some(450789469));
1000        assert_eq!(order.name.as_deref(), Some("#1001"));
1001        assert_eq!(order.email.as_deref(), Some("customer@example.com"));
1002        assert_eq!(order.order_number, Some(1001));
1003        assert_eq!(order.financial_status, Some(FinancialStatus::Paid));
1004        assert_eq!(order.fulfillment_status, Some(FulfillmentStatus::Partial));
1005
1006        // Check line items
1007        let line_items = order.line_items.unwrap();
1008        assert_eq!(line_items.len(), 1);
1009        assert_eq!(line_items[0].id, Some(669751112));
1010        assert_eq!(line_items[0].title.as_deref(), Some("IPod Nano - 8GB"));
1011        assert_eq!(line_items[0].quantity, Some(1));
1012
1013        // Check nested tax_lines in line_item
1014        let tax_lines = line_items[0].tax_lines.as_ref().unwrap();
1015        assert_eq!(tax_lines.len(), 1);
1016        assert_eq!(tax_lines[0].title.as_deref(), Some("State Tax"));
1017
1018        // Check billing address
1019        let billing = order.billing_address.unwrap();
1020        assert_eq!(billing.first_name.as_deref(), Some("John"));
1021        assert_eq!(billing.city.as_deref(), Some("New York"));
1022
1023        // Check customer
1024        let customer = order.customer.unwrap();
1025        assert_eq!(customer.id, Some(207119551));
1026        assert_eq!(customer.first_name.as_deref(), Some("John"));
1027    }
1028
1029    #[test]
1030    fn test_financial_status_enum_serialization() {
1031        // Test serialization
1032        assert_eq!(
1033            serde_json::to_string(&FinancialStatus::Pending).unwrap(),
1034            "\"pending\""
1035        );
1036        assert_eq!(
1037            serde_json::to_string(&FinancialStatus::Authorized).unwrap(),
1038            "\"authorized\""
1039        );
1040        assert_eq!(
1041            serde_json::to_string(&FinancialStatus::PartiallyPaid).unwrap(),
1042            "\"partially_paid\""
1043        );
1044        assert_eq!(
1045            serde_json::to_string(&FinancialStatus::Paid).unwrap(),
1046            "\"paid\""
1047        );
1048        assert_eq!(
1049            serde_json::to_string(&FinancialStatus::PartiallyRefunded).unwrap(),
1050            "\"partially_refunded\""
1051        );
1052        assert_eq!(
1053            serde_json::to_string(&FinancialStatus::Refunded).unwrap(),
1054            "\"refunded\""
1055        );
1056        assert_eq!(
1057            serde_json::to_string(&FinancialStatus::Voided).unwrap(),
1058            "\"voided\""
1059        );
1060
1061        // Test deserialization
1062        let paid: FinancialStatus = serde_json::from_str("\"paid\"").unwrap();
1063        let partially_refunded: FinancialStatus =
1064            serde_json::from_str("\"partially_refunded\"").unwrap();
1065
1066        assert_eq!(paid, FinancialStatus::Paid);
1067        assert_eq!(partially_refunded, FinancialStatus::PartiallyRefunded);
1068
1069        // Test default
1070        assert_eq!(FinancialStatus::default(), FinancialStatus::Pending);
1071    }
1072
1073    #[test]
1074    fn test_fulfillment_status_enum_serialization() {
1075        // Test serialization
1076        assert_eq!(
1077            serde_json::to_string(&FulfillmentStatus::Fulfilled).unwrap(),
1078            "\"fulfilled\""
1079        );
1080        assert_eq!(
1081            serde_json::to_string(&FulfillmentStatus::Partial).unwrap(),
1082            "\"partial\""
1083        );
1084        assert_eq!(
1085            serde_json::to_string(&FulfillmentStatus::Unfulfilled).unwrap(),
1086            "\"unfulfilled\""
1087        );
1088        assert_eq!(
1089            serde_json::to_string(&FulfillmentStatus::Restocked).unwrap(),
1090            "\"restocked\""
1091        );
1092
1093        // Test deserialization
1094        let fulfilled: FulfillmentStatus = serde_json::from_str("\"fulfilled\"").unwrap();
1095        let partial: FulfillmentStatus = serde_json::from_str("\"partial\"").unwrap();
1096
1097        assert_eq!(fulfilled, FulfillmentStatus::Fulfilled);
1098        assert_eq!(partial, FulfillmentStatus::Partial);
1099    }
1100
1101    #[test]
1102    fn test_cancel_reason_enum_serialization() {
1103        // Test serialization
1104        assert_eq!(
1105            serde_json::to_string(&CancelReason::Customer).unwrap(),
1106            "\"customer\""
1107        );
1108        assert_eq!(
1109            serde_json::to_string(&CancelReason::Fraud).unwrap(),
1110            "\"fraud\""
1111        );
1112        assert_eq!(
1113            serde_json::to_string(&CancelReason::Inventory).unwrap(),
1114            "\"inventory\""
1115        );
1116        assert_eq!(
1117            serde_json::to_string(&CancelReason::Declined).unwrap(),
1118            "\"declined\""
1119        );
1120        assert_eq!(
1121            serde_json::to_string(&CancelReason::Other).unwrap(),
1122            "\"other\""
1123        );
1124
1125        // Test deserialization
1126        let fraud: CancelReason = serde_json::from_str("\"fraud\"").unwrap();
1127        let inventory: CancelReason = serde_json::from_str("\"inventory\"").unwrap();
1128
1129        assert_eq!(fraud, CancelReason::Fraud);
1130        assert_eq!(inventory, CancelReason::Inventory);
1131    }
1132
1133    #[test]
1134    fn test_order_list_params_with_status_filters() {
1135        let created_at_min = DateTime::parse_from_rfc3339("2024-01-01T00:00:00Z")
1136            .unwrap()
1137            .with_timezone(&Utc);
1138
1139        let params = OrderListParams {
1140            ids: Some(vec![123, 456, 789]),
1141            limit: Some(50),
1142            since_id: Some(100),
1143            status: Some("open".to_string()),
1144            financial_status: Some(FinancialStatus::Paid),
1145            fulfillment_status: Some(FulfillmentStatus::Unfulfilled),
1146            created_at_min: Some(created_at_min),
1147            created_at_max: None,
1148            updated_at_min: None,
1149            updated_at_max: None,
1150            processed_at_min: None,
1151            processed_at_max: None,
1152            attribution_app_id: Some(12345),
1153            fields: Some("id,name,total_price".to_string()),
1154            page_info: None,
1155        };
1156
1157        let json = serde_json::to_value(&params).unwrap();
1158
1159        assert_eq!(json["ids"], serde_json::json!([123, 456, 789]));
1160        assert_eq!(json["limit"], 50);
1161        assert_eq!(json["since_id"], 100);
1162        assert_eq!(json["status"], "open");
1163        assert_eq!(json["financial_status"], "paid");
1164        assert_eq!(json["fulfillment_status"], "unfulfilled");
1165        assert_eq!(json["attribution_app_id"], 12345);
1166        assert_eq!(json["fields"], "id,name,total_price");
1167        assert!(json["created_at_min"].as_str().is_some());
1168
1169        // Test empty params
1170        let empty_params = OrderListParams::default();
1171        let empty_json = serde_json::to_value(&empty_params).unwrap();
1172        assert_eq!(empty_json, serde_json::json!({}));
1173    }
1174
1175    #[test]
1176    fn test_order_resource_specific_operations_signatures() {
1177        // This test verifies that the cancel, close, and open methods exist
1178        // with the correct signatures. The actual HTTP calls would require
1179        // a mock client, but we verify the type signatures compile correctly.
1180
1181        // Verify the method signatures are correct by referencing them
1182        fn _assert_cancel_signature<F, Fut>(f: F)
1183        where
1184            F: Fn(&Order, &RestClient) -> Fut,
1185            Fut: std::future::Future<Output = Result<Order, ResourceError>>,
1186        {
1187            let _ = f;
1188        }
1189
1190        fn _assert_close_signature<F, Fut>(f: F)
1191        where
1192            F: Fn(&Order, &RestClient) -> Fut,
1193            Fut: std::future::Future<Output = Result<Order, ResourceError>>,
1194        {
1195            let _ = f;
1196        }
1197
1198        fn _assert_open_signature<F, Fut>(f: F)
1199        where
1200            F: Fn(&Order, &RestClient) -> Fut,
1201            Fut: std::future::Future<Output = Result<Order, ResourceError>>,
1202        {
1203            let _ = f;
1204        }
1205
1206        // Verify PathResolutionFailed error is returned when order has no ID
1207        let order_without_id = Order::default();
1208        assert!(order_without_id.get_id().is_none());
1209
1210        // The actual async tests would require a mock RestClient
1211        // For now, we just verify the types compile
1212    }
1213
1214    #[test]
1215    fn test_order_get_id_returns_correct_value() {
1216        let order_with_id = Order {
1217            id: Some(450789469),
1218            name: Some("#1001".to_string()),
1219            ..Default::default()
1220        };
1221        assert_eq!(order_with_id.get_id(), Some(450789469));
1222
1223        let order_without_id = Order {
1224            id: None,
1225            email: Some("new@example.com".to_string()),
1226            ..Default::default()
1227        };
1228        assert_eq!(order_without_id.get_id(), None);
1229    }
1230
1231    #[test]
1232    fn test_order_path_constants_are_correct() {
1233        let find_path = get_path(Order::PATHS, ResourceOperation::Find, &["id"]);
1234        assert!(find_path.is_some());
1235        assert_eq!(find_path.unwrap().template, "orders/{id}");
1236        assert_eq!(find_path.unwrap().http_method, HttpMethod::Get);
1237
1238        let all_path = get_path(Order::PATHS, ResourceOperation::All, &[]);
1239        assert!(all_path.is_some());
1240        assert_eq!(all_path.unwrap().template, "orders");
1241
1242        let count_path = get_path(Order::PATHS, ResourceOperation::Count, &[]);
1243        assert!(count_path.is_some());
1244        assert_eq!(count_path.unwrap().template, "orders/count");
1245
1246        let create_path = get_path(Order::PATHS, ResourceOperation::Create, &[]);
1247        assert!(create_path.is_some());
1248        assert_eq!(create_path.unwrap().http_method, HttpMethod::Post);
1249
1250        let update_path = get_path(Order::PATHS, ResourceOperation::Update, &["id"]);
1251        assert!(update_path.is_some());
1252        assert_eq!(update_path.unwrap().http_method, HttpMethod::Put);
1253
1254        let delete_path = get_path(Order::PATHS, ResourceOperation::Delete, &["id"]);
1255        assert!(delete_path.is_some());
1256        assert_eq!(delete_path.unwrap().http_method, HttpMethod::Delete);
1257
1258        assert_eq!(Order::NAME, "Order");
1259        assert_eq!(Order::PLURAL, "orders");
1260    }
1261
1262    #[test]
1263    fn test_discount_code_serialization() {
1264        let discount = DiscountCode {
1265            code: Some("SAVE10".to_string()),
1266            amount: Some("10.00".to_string()),
1267            discount_type: Some("fixed_amount".to_string()),
1268        };
1269
1270        let json = serde_json::to_string(&discount).unwrap();
1271        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
1272
1273        assert_eq!(parsed["code"], "SAVE10");
1274        assert_eq!(parsed["amount"], "10.00");
1275        assert_eq!(parsed["type"], "fixed_amount");
1276
1277        // Test deserialization with renamed field
1278        let json_str = r#"{"code":"SUMMER20","amount":"20.00","type":"percentage"}"#;
1279        let parsed_discount: DiscountCode = serde_json::from_str(json_str).unwrap();
1280        assert_eq!(parsed_discount.discount_type.as_deref(), Some("percentage"));
1281    }
1282
1283    #[test]
1284    fn test_refund_struct_serialization() {
1285        let refund = Refund {
1286            id: Some(123456),
1287            order_id: Some(450789469),
1288            note: Some("Customer requested".to_string()),
1289            restock: Some(true),
1290            ..Default::default()
1291        };
1292
1293        let json = serde_json::to_string(&refund).unwrap();
1294        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
1295
1296        assert_eq!(parsed["id"], 123456);
1297        assert_eq!(parsed["order_id"], 450789469);
1298        assert_eq!(parsed["note"], "Customer requested");
1299        assert_eq!(parsed["restock"], true);
1300    }
1301
1302    #[test]
1303    fn test_order_with_all_nested_structures() {
1304        // Use r##"..."## to allow # characters in the JSON string
1305        let json_str = r##"{
1306            "id": 450789469,
1307            "name": "#1001",
1308            "email": "customer@example.com",
1309            "financial_status": "partially_refunded",
1310            "line_items": [
1311                {"id": 1, "title": "Product 1", "quantity": 2}
1312            ],
1313            "billing_address": {
1314                "first_name": "John",
1315                "city": "New York"
1316            },
1317            "shipping_address": {
1318                "first_name": "John",
1319                "city": "New York"
1320            },
1321            "tax_lines": [
1322                {"title": "Tax", "price": "10.00", "rate": 0.1}
1323            ],
1324            "discount_codes": [
1325                {"code": "SAVE10", "amount": "10.00", "type": "fixed_amount"}
1326            ],
1327            "discount_applications": [
1328                {"type": "discount_code", "value": "10.00", "code": "SAVE10"}
1329            ],
1330            "shipping_lines": [
1331                {"id": 1, "title": "Standard", "price": "5.00"}
1332            ],
1333            "fulfillments": [
1334                {"id": 1, "status": "success", "tracking_number": "1234"}
1335            ],
1336            "refunds": [
1337                {"id": 1, "note": "Partial refund"}
1338            ],
1339            "customer": {
1340                "id": 207119551,
1341                "email": "customer@example.com"
1342            }
1343        }"##;
1344
1345        let order: Order = serde_json::from_str(json_str).unwrap();
1346
1347        assert!(order.line_items.is_some());
1348        assert!(order.billing_address.is_some());
1349        assert!(order.shipping_address.is_some());
1350        assert!(order.tax_lines.is_some());
1351        assert!(order.discount_codes.is_some());
1352        assert!(order.discount_applications.is_some());
1353        assert!(order.shipping_lines.is_some());
1354        assert!(order.fulfillments.is_some());
1355        assert!(order.refunds.is_some());
1356        assert!(order.customer.is_some());
1357
1358        assert_eq!(
1359            order.financial_status,
1360            Some(FinancialStatus::PartiallyRefunded)
1361        );
1362        assert_eq!(
1363            order.discount_codes.as_ref().unwrap()[0].code.as_deref(),
1364            Some("SAVE10")
1365        );
1366        assert_eq!(
1367            order.fulfillments.as_ref().unwrap()[0]
1368                .tracking_number
1369                .as_deref(),
1370            Some("1234")
1371        );
1372    }
1373}