1use 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#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
55#[serde(rename_all = "snake_case")]
56pub enum FinancialStatus {
57 #[default]
59 Pending,
60 Authorized,
62 PartiallyPaid,
64 Paid,
66 PartiallyRefunded,
68 Refunded,
70 Voided,
72}
73
74#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
78#[serde(rename_all = "snake_case")]
79pub enum FulfillmentStatus {
80 Fulfilled,
82 Partial,
84 Unfulfilled,
86 Restocked,
88}
89
90#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
92#[serde(rename_all = "snake_case")]
93pub enum CancelReason {
94 Customer,
96 Fraud,
98 Inventory,
100 Declined,
102 Other,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
111pub struct DiscountCode {
112 #[serde(skip_serializing_if = "Option::is_none")]
114 pub code: Option<String>,
115
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub amount: Option<String>,
119
120 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
122 pub discount_type: Option<String>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
129pub struct Refund {
130 #[serde(skip_serializing_if = "Option::is_none")]
132 pub id: Option<u64>,
133
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub order_id: Option<u64>,
137
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub created_at: Option<DateTime<Utc>>,
141
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub note: Option<String>,
145
146 #[serde(skip_serializing_if = "Option::is_none")]
148 pub user_id: Option<u64>,
149
150 #[serde(skip_serializing_if = "Option::is_none")]
152 pub processed_at: Option<DateTime<Utc>>,
153
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub restock: Option<bool>,
157
158 #[serde(skip_serializing_if = "Option::is_none")]
160 pub duties: Option<serde_json::Value>,
161
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub refund_duties: Option<serde_json::Value>,
165
166 #[serde(skip_serializing_if = "Option::is_none")]
168 pub refund_line_items: Option<serde_json::Value>,
169
170 #[serde(skip_serializing_if = "Option::is_none")]
172 pub transactions: Option<serde_json::Value>,
173
174 #[serde(skip_serializing_if = "Option::is_none")]
176 pub order_adjustments: Option<serde_json::Value>,
177
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub admin_graphql_api_id: Option<String>,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
187pub struct OrderFulfillment {
188 #[serde(skip_serializing_if = "Option::is_none")]
190 pub id: Option<u64>,
191
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub order_id: Option<u64>,
195
196 #[serde(skip_serializing_if = "Option::is_none")]
198 pub status: Option<String>,
199
200 #[serde(skip_serializing_if = "Option::is_none")]
202 pub created_at: Option<DateTime<Utc>>,
203
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub service: Option<String>,
207
208 #[serde(skip_serializing_if = "Option::is_none")]
210 pub updated_at: Option<DateTime<Utc>>,
211
212 #[serde(skip_serializing_if = "Option::is_none")]
214 pub tracking_company: Option<String>,
215
216 #[serde(skip_serializing_if = "Option::is_none")]
218 pub shipment_status: Option<String>,
219
220 #[serde(skip_serializing_if = "Option::is_none")]
222 pub location_id: Option<u64>,
223
224 #[serde(skip_serializing_if = "Option::is_none")]
226 pub tracking_number: Option<String>,
227
228 #[serde(skip_serializing_if = "Option::is_none")]
230 pub tracking_numbers: Option<Vec<String>>,
231
232 #[serde(skip_serializing_if = "Option::is_none")]
234 pub tracking_url: Option<String>,
235
236 #[serde(skip_serializing_if = "Option::is_none")]
238 pub tracking_urls: Option<Vec<String>>,
239
240 #[serde(skip_serializing_if = "Option::is_none")]
242 pub line_items: Option<serde_json::Value>,
243
244 #[serde(skip_serializing_if = "Option::is_none")]
246 pub admin_graphql_api_id: Option<String>,
247}
248
249#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
273pub struct Order {
274 #[serde(skip_serializing)]
277 pub id: Option<u64>,
278
279 #[serde(skip_serializing)]
281 pub name: Option<String>,
282
283 #[serde(skip_serializing)]
285 pub order_number: Option<u64>,
286
287 #[serde(skip_serializing)]
289 pub created_at: Option<DateTime<Utc>>,
290
291 #[serde(skip_serializing)]
293 pub updated_at: Option<DateTime<Utc>>,
294
295 #[serde(skip_serializing)]
297 pub confirmation_number: Option<String>,
298
299 #[serde(skip_serializing)]
301 pub admin_graphql_api_id: Option<String>,
302
303 #[serde(skip_serializing_if = "Option::is_none")]
306 pub email: Option<String>,
307
308 #[serde(skip_serializing_if = "Option::is_none")]
310 pub phone: Option<String>,
311
312 #[serde(skip_serializing_if = "Option::is_none")]
314 pub closed_at: Option<DateTime<Utc>>,
315
316 #[serde(skip_serializing_if = "Option::is_none")]
318 pub cancelled_at: Option<DateTime<Utc>>,
319
320 #[serde(skip_serializing_if = "Option::is_none")]
322 pub cancel_reason: Option<CancelReason>,
323
324 #[serde(skip_serializing_if = "Option::is_none")]
326 pub financial_status: Option<FinancialStatus>,
327
328 #[serde(skip_serializing_if = "Option::is_none")]
330 pub fulfillment_status: Option<FulfillmentStatus>,
331
332 #[serde(skip_serializing_if = "Option::is_none")]
334 pub currency: Option<String>,
335
336 #[serde(skip_serializing_if = "Option::is_none")]
338 pub total_price: Option<String>,
339
340 #[serde(skip_serializing_if = "Option::is_none")]
342 pub subtotal_price: Option<String>,
343
344 #[serde(skip_serializing_if = "Option::is_none")]
346 pub total_tax: Option<String>,
347
348 #[serde(skip_serializing_if = "Option::is_none")]
350 pub total_discounts: Option<String>,
351
352 #[serde(skip_serializing_if = "Option::is_none")]
354 pub total_weight: Option<i64>,
355
356 #[serde(skip_serializing_if = "Option::is_none")]
358 pub taxes_included: Option<bool>,
359
360 #[serde(skip_serializing_if = "Option::is_none")]
362 pub buyer_accepts_marketing: Option<bool>,
363
364 #[serde(skip_serializing_if = "Option::is_none")]
366 pub note: Option<String>,
367
368 #[serde(skip_serializing_if = "Option::is_none")]
370 pub note_attributes: Option<Vec<NoteAttribute>>,
371
372 #[serde(skip_serializing_if = "Option::is_none")]
374 pub tags: Option<String>,
375
376 #[serde(skip_serializing_if = "Option::is_none")]
378 pub app_id: Option<u64>,
379
380 #[serde(skip_serializing_if = "Option::is_none")]
382 pub browser_ip: Option<String>,
383
384 #[serde(skip_serializing_if = "Option::is_none")]
386 pub customer_locale: Option<String>,
387
388 #[serde(skip_serializing_if = "Option::is_none")]
390 pub order_status_url: Option<String>,
391
392 #[serde(skip_serializing_if = "Option::is_none")]
394 pub processed_at: Option<DateTime<Utc>>,
395
396 #[serde(skip_serializing_if = "Option::is_none")]
398 pub source_name: Option<String>,
399
400 #[serde(skip_serializing_if = "Option::is_none")]
402 pub total_price_usd: Option<String>,
403
404 #[serde(skip_serializing_if = "Option::is_none")]
406 pub total_shipping_price_set: Option<serde_json::Value>,
407
408 #[serde(skip_serializing_if = "Option::is_none")]
410 pub total_line_items_price: Option<String>,
411
412 #[serde(skip_serializing_if = "Option::is_none")]
414 pub total_outstanding: Option<String>,
415
416 #[serde(skip_serializing_if = "Option::is_none")]
418 pub current_total_price: Option<String>,
419
420 #[serde(skip_serializing_if = "Option::is_none")]
422 pub current_subtotal_price: Option<String>,
423
424 #[serde(skip_serializing_if = "Option::is_none")]
426 pub current_total_tax: Option<String>,
427
428 #[serde(skip_serializing_if = "Option::is_none")]
430 pub current_total_discounts: Option<String>,
431
432 #[serde(skip_serializing_if = "Option::is_none")]
434 pub confirmed: Option<bool>,
435
436 #[serde(skip_serializing_if = "Option::is_none")]
438 pub test: Option<bool>,
439
440 #[serde(skip_serializing_if = "Option::is_none")]
442 pub user_id: Option<u64>,
443
444 #[serde(skip_serializing_if = "Option::is_none")]
446 pub location_id: Option<u64>,
447
448 #[serde(skip_serializing_if = "Option::is_none")]
450 pub source_identifier: Option<String>,
451
452 #[serde(skip_serializing_if = "Option::is_none")]
454 pub source_url: Option<String>,
455
456 #[serde(skip_serializing_if = "Option::is_none")]
458 pub device_id: Option<u64>,
459
460 #[serde(skip_serializing_if = "Option::is_none")]
462 pub landing_site: Option<String>,
463
464 #[serde(skip_serializing_if = "Option::is_none")]
466 pub referring_site: Option<String>,
467
468 #[serde(skip_serializing_if = "Option::is_none")]
470 pub gateway: Option<String>,
471
472 #[serde(skip_serializing_if = "Option::is_none")]
474 pub payment_gateway_names: Option<Vec<String>>,
475
476 #[serde(skip_serializing_if = "Option::is_none")]
478 pub processing_method: Option<String>,
479
480 #[serde(skip_serializing_if = "Option::is_none")]
482 pub reference: Option<String>,
483
484 #[serde(skip_serializing_if = "Option::is_none")]
486 pub checkout_id: Option<u64>,
487
488 #[serde(skip_serializing_if = "Option::is_none")]
490 pub checkout_token: Option<String>,
491
492 #[serde(skip_serializing_if = "Option::is_none")]
494 pub cart_token: Option<String>,
495
496 #[serde(skip_serializing_if = "Option::is_none")]
498 pub token: Option<String>,
499
500 #[serde(skip_serializing_if = "Option::is_none")]
503 pub line_items: Option<Vec<LineItem>>,
504
505 #[serde(skip_serializing_if = "Option::is_none")]
507 pub billing_address: Option<Address>,
508
509 #[serde(skip_serializing_if = "Option::is_none")]
511 pub shipping_address: Option<Address>,
512
513 #[serde(skip_serializing_if = "Option::is_none")]
515 pub tax_lines: Option<Vec<TaxLine>>,
516
517 #[serde(skip_serializing_if = "Option::is_none")]
519 pub discount_codes: Option<Vec<DiscountCode>>,
520
521 #[serde(skip_serializing_if = "Option::is_none")]
523 pub discount_applications: Option<Vec<DiscountApplication>>,
524
525 #[serde(skip_serializing_if = "Option::is_none")]
527 pub shipping_lines: Option<Vec<ShippingLine>>,
528
529 #[serde(skip_serializing_if = "Option::is_none")]
531 pub fulfillments: Option<Vec<OrderFulfillment>>,
532
533 #[serde(skip_serializing_if = "Option::is_none")]
535 pub refunds: Option<Vec<Refund>>,
536
537 #[serde(skip_serializing_if = "Option::is_none")]
539 pub customer: Option<Customer>,
540
541 #[serde(skip_serializing_if = "Option::is_none")]
543 pub client_details: Option<serde_json::Value>,
544
545 #[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 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 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 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 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 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 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#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
804pub struct OrderFindParams {
805 #[serde(skip_serializing_if = "Option::is_none")]
807 pub fields: Option<String>,
808}
809
810#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
814pub struct OrderListParams {
815 #[serde(skip_serializing_if = "Option::is_none")]
817 pub ids: Option<Vec<u64>>,
818
819 #[serde(skip_serializing_if = "Option::is_none")]
821 pub limit: Option<u32>,
822
823 #[serde(skip_serializing_if = "Option::is_none")]
825 pub since_id: Option<u64>,
826
827 #[serde(skip_serializing_if = "Option::is_none")]
829 pub status: Option<String>,
830
831 #[serde(skip_serializing_if = "Option::is_none")]
833 pub financial_status: Option<FinancialStatus>,
834
835 #[serde(skip_serializing_if = "Option::is_none")]
837 pub fulfillment_status: Option<FulfillmentStatus>,
838
839 #[serde(skip_serializing_if = "Option::is_none")]
841 pub created_at_min: Option<DateTime<Utc>>,
842
843 #[serde(skip_serializing_if = "Option::is_none")]
845 pub created_at_max: Option<DateTime<Utc>>,
846
847 #[serde(skip_serializing_if = "Option::is_none")]
849 pub updated_at_min: Option<DateTime<Utc>>,
850
851 #[serde(skip_serializing_if = "Option::is_none")]
853 pub updated_at_max: Option<DateTime<Utc>>,
854
855 #[serde(skip_serializing_if = "Option::is_none")]
857 pub processed_at_min: Option<DateTime<Utc>>,
858
859 #[serde(skip_serializing_if = "Option::is_none")]
861 pub processed_at_max: Option<DateTime<Utc>>,
862
863 #[serde(skip_serializing_if = "Option::is_none")]
865 pub attribution_app_id: Option<u64>,
866
867 #[serde(skip_serializing_if = "Option::is_none")]
869 pub fields: Option<String>,
870
871 #[serde(skip_serializing_if = "Option::is_none")]
873 pub page_info: Option<String>,
874}
875
876#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
878pub struct OrderCountParams {
879 #[serde(skip_serializing_if = "Option::is_none")]
881 pub status: Option<String>,
882
883 #[serde(skip_serializing_if = "Option::is_none")]
885 pub financial_status: Option<FinancialStatus>,
886
887 #[serde(skip_serializing_if = "Option::is_none")]
889 pub fulfillment_status: Option<FulfillmentStatus>,
890
891 #[serde(skip_serializing_if = "Option::is_none")]
893 pub created_at_min: Option<DateTime<Utc>>,
894
895 #[serde(skip_serializing_if = "Option::is_none")]
897 pub created_at_max: Option<DateTime<Utc>>,
898
899 #[serde(skip_serializing_if = "Option::is_none")]
901 pub updated_at_min: Option<DateTime<Utc>>,
902
903 #[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 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 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 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 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 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 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 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 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 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 assert_eq!(FinancialStatus::default(), FinancialStatus::Pending);
1071 }
1072
1073 #[test]
1074 fn test_fulfillment_status_enum_serialization() {
1075 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 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 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 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(¶ms).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 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 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 let order_without_id = Order::default();
1208 assert!(order_without_id.get_id().is_none());
1209
1210 }
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 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 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}