1 | |
2 |
|
3 | |
4 | * Create Fulfillment |
5 | * Fulfill one or more fulfillment orders of the same order and location (mark items as shipped). Fulfills every remaining item, or only line_items ({ id, quantity } of fulfillment order line items) when given, which needs a single fulfillment order. Optional tracking info; notify_customer sends the shipping confirmation email. |
6 | */ |
7 | export async function main( |
8 | auth: RT.Shopify, |
9 | fulfillment_order_ids: string[], |
10 | line_items: { id: string; quantity: number }[] | undefined, |
11 | tracking_number: string | undefined, |
12 | tracking_url: string | undefined, |
13 | tracking_company: string | undefined, |
14 | notify_customer: boolean | undefined, |
15 | message: string | undefined |
16 | ) { |
17 | const foIds = fulfillment_order_ids.map((id) => |
18 | id.startsWith("gid://") ? id : `gid://shopify/FulfillmentOrder/${id}` |
19 | ) |
20 | const lineItems = line_items?.length |
21 | ? line_items.map((li) => ({ |
22 | id: li.id.startsWith("gid://") |
23 | ? li.id |
24 | : `gid://shopify/FulfillmentOrderLineItem/${li.id}`, |
25 | quantity: li.quantity, |
26 | })) |
27 | : undefined |
28 | if (lineItems && foIds.length > 1) { |
29 | throw new Error( |
30 | "line_items can only be used with a single fulfillment order" |
31 | ) |
32 | } |
33 | const hasTracking = tracking_number || tracking_url || tracking_company |
34 |
|
35 | const response = await fetch( |
36 | `https://${auth.store_name}.myshopify.com/admin/api/2026-10/graphql.json`, |
37 | { |
38 | method: "POST", |
39 | headers: { |
40 | "X-Shopify-Access-Token": auth.token, |
41 | "Content-Type": "application/json", |
42 | Accept: "application/json", |
43 | }, |
44 | body: JSON.stringify({ |
45 | query: `mutation ($fulfillment: FulfillmentInput!, $message: String) { fulfillmentCreate(fulfillment: $fulfillment, message: $message) { fulfillment { id legacyResourceId name status displayStatus createdAt updatedAt deliveredAt estimatedDeliveryAt inTransitAt totalQuantity requiresShipping trackingInfo { company number url } location { id name } order { id name } service { handle serviceName } fulfillmentLineItems(first: 250) { nodes { id quantity lineItem { id name sku } } } fulfillmentOrders(first: 10) { nodes { id status } } } userErrors { field message } } }`, |
46 | variables: { |
47 | fulfillment: { |
48 | lineItemsByFulfillmentOrder: foIds.map((fulfillmentOrderId) => ({ |
49 | fulfillmentOrderId, |
50 | fulfillmentOrderLineItems: lineItems, |
51 | })), |
52 | trackingInfo: hasTracking |
53 | ? { |
54 | number: tracking_number || undefined, |
55 | url: tracking_url || undefined, |
56 | company: tracking_company || undefined, |
57 | } |
58 | : undefined, |
59 | notifyCustomer: notify_customer || undefined, |
60 | }, |
61 | message: message || undefined, |
62 | }, |
63 | }), |
64 | } |
65 | ) |
66 |
|
67 | if (!response.ok) { |
68 | throw new Error(`${response.status} ${await response.text()}`) |
69 | } |
70 |
|
71 | const { data, errors } = await response.json() |
72 | if (errors) { |
73 | throw new Error(JSON.stringify(errors)) |
74 | } |
75 |
|
76 | if (data.fulfillmentCreate.userErrors.length) { |
77 | throw new Error(JSON.stringify(data.fulfillmentCreate.userErrors)) |
78 | } |
79 |
|
80 | return data.fulfillmentCreate |
81 | } |
82 |
|