@fluentcommerce/ai-skills 0.8.2 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -29
- package/bin/cli.mjs +278 -10
- package/content/cli/skills/fluent-cli-mcp-cicd/SKILL.md +6 -3
- package/content/cli/skills/fluent-connect/SKILL.md +1 -1
- package/content/dev/skills/fluent-e2e-test/SKILL.md +3 -1
- package/content/dev/skills/fluent-event-api/SKILL.md +1 -0
- package/content/dev/skills/fluent-feature-explain/SKILL.md +1 -0
- package/content/dev/skills/fluent-feature-plan/SKILL.md +1 -0
- package/content/dev/skills/fluent-mystique-builder/SKILL.md +2 -3
- package/content/dev/skills/fluent-mystique-scaffold/SDK_REFERENCE.md +2 -2
- package/content/dev/skills/fluent-mystique-scaffold/TEMPLATES.md +1 -2
- package/content/dev/skills/fluent-mystique-sdk-reference/SKILL.md +2 -2
- package/content/dev/skills/fluent-pre-deploy-check/SKILL.md +25 -0
- package/content/dev/skills/fluent-sourcing/SKILL.md +4 -0
- package/content/dev/skills/fluent-test-data/SKILL.md +4 -0
- package/content/dev/skills/fluent-trace/SKILL.md +16 -1
- package/content/dev/skills/fluent-workflow-builder/SKILL.md +11 -1
- package/content/dev/skills/fluent-workflow-deploy/SKILL.md +27 -0
- package/content/knowledge/index.md +2 -0
- package/content/knowledge/platform/create-order-reference.md +797 -0
- package/content/knowledge/platform/domain-model.md +1 -1
- package/content/knowledge/platform/workflow-design.md +18 -0
- package/content/mcp-extn/skills/fluent-mcp-core/SKILL.md +1 -1
- package/content/mcp-extn/skills/fluent-mcp-tools/SKILL.md +1 -1
- package/docs/dev-workflow.md +5 -2
- package/docs/fluent-ai-skills-reference.md +2 -2
- package/docs/getting-started.md +2 -2
- package/docs/use-cases.md +1 -1
- package/docs/workflow-reference.md +4 -2
- package/metadata.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,797 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-order-reference
|
|
3
|
+
scope: platform
|
|
4
|
+
description: "Schema-validated createOrder reference covering FulfilmentChoice linkage, ORDER::MULTI patterns, and readback query shapes."
|
|
5
|
+
load: on-demand
|
|
6
|
+
priority: high
|
|
7
|
+
relevant-skills: [fluent-test-data, fluent-e2e-test, fluent-trace, fluent-event-api, fluent-workflow-builder, fluent-feature-plan, fluent-feature-explain]
|
|
8
|
+
relevant-entities: [ORDER, FULFILMENT_CHOICE, FULFILMENT, CUSTOMER, PRODUCT, PAYMENT]
|
|
9
|
+
version: 1.1
|
|
10
|
+
last-updated: 2026-03-23
|
|
11
|
+
author: schema-introspection
|
|
12
|
+
source: live-schema + https://docs.fluentcommerce.com/by-type/create-order-using-graphql-mutation + https://docs.fluentcommerce.com/essential-knowledge/fulfilment-choice-entity
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# createOrder Mutation Reference
|
|
16
|
+
|
|
17
|
+
Complete reference for the `createOrder` GraphQL mutation. All input and readback examples were re-validated against the live Fluent Commerce schema on 2026-03-23, with official Fluent docs used as the behavioral cross-check for mixed-basket (`ORDER::MULTI`) patterns.
|
|
18
|
+
|
|
19
|
+
## Mutation Signature
|
|
20
|
+
|
|
21
|
+
```graphql
|
|
22
|
+
mutation($input: CreateOrderInput!) {
|
|
23
|
+
createOrder(input: $input) {
|
|
24
|
+
id ref status type
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Type Tree
|
|
32
|
+
|
|
33
|
+
### CreateOrderInput (root)
|
|
34
|
+
|
|
35
|
+
| Field | Type | Required | Notes |
|
|
36
|
+
|-------|------|:--------:|-------|
|
|
37
|
+
| `ref` | `String!` | YES | Must be unique across the account |
|
|
38
|
+
| `type` | `String!` | YES | Workflow routing key (e.g., `HD`, `CC`, `MULTI`) |
|
|
39
|
+
| `retailer` | `RetailerId!` | YES | **Wrapper object** `{ id: <ID> }` — NOT a bare ID or ref |
|
|
40
|
+
| `customer` | `CustomerId!` | YES | **Wrapper object** `{ id: <ID> }` — NOT a bare ID or ref |
|
|
41
|
+
| `items` | `[CreateOrderItemWithOrderInput]!` | YES | At least one item required |
|
|
42
|
+
| `fulfilmentChoice` | `CreateFulfilmentChoiceWithOrderInput` | no | **Singular** — use for single-FC orders |
|
|
43
|
+
| `fulfilmentChoices` | `[CreateFulfilmentChoiceWithOrderInput]` | no | **Plural** — use for split-shipment / mixed-FC orders |
|
|
44
|
+
| `totalPrice` | `Float` | no | Order total (excl. tax) |
|
|
45
|
+
| `totalTaxPrice` | `Float` | no | Tax total |
|
|
46
|
+
| `billingAddress` | `CreateOrderBillingAddressInput` | no | Billing address (all fields optional) |
|
|
47
|
+
| `payment` | `PaymentKey` | no | `{ ref: "PAY-REF" }` — links to existing Payment entity |
|
|
48
|
+
| `financialTransactions` | `[CreateFinancialTransactionWithOrderInput]` | no | Inline payment records |
|
|
49
|
+
| `attributes` | `[AttributeInput]` | no | Custom metadata |
|
|
50
|
+
| `ref2` | `String` | no | Secondary reference (must be unique if provided) |
|
|
51
|
+
| `tag1` / `tag2` / `tag3` | `String` | no | Searchable tags |
|
|
52
|
+
|
|
53
|
+
### RetailerId / CustomerId (wrapper objects)
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
"retailer": { "id": "123" },
|
|
57
|
+
"customer": { "id": "456" }
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
These are **wrapper input types** with a single `id: ID!` field. Reuse the exact `id` value returned by discovery and do **not** substitute `ref`. GraphQL `ID` accepts scalar ID values, and Fluent queries commonly return them as strings. Discover via:
|
|
61
|
+
- Retailer: `retailers(first:1) { edges { node { id ref tradingName } } }`
|
|
62
|
+
- Customer: `customers(first:1, username: {...}) { edges { node { id firstName lastName } } }`
|
|
63
|
+
|
|
64
|
+
### CreateOrderItemWithOrderInput
|
|
65
|
+
|
|
66
|
+
| Field | Type | Required | Notes |
|
|
67
|
+
|-------|------|:--------:|-------|
|
|
68
|
+
| `ref` | `String!` | YES | Unique within the order |
|
|
69
|
+
| `productRef` | `String!` | YES | Must exist in the catalogue |
|
|
70
|
+
| `quantity` | `Int!` | YES | Must be >= 1 |
|
|
71
|
+
| `productCatalogueRef` | `String` | no | Which catalogue contains the product. Omit only if using the compatibility catalogue |
|
|
72
|
+
| `fulfilmentChoiceRef` | `String` | no | **Links this item to a specific FulfilmentChoice by ref**. Required for split-shipment orders |
|
|
73
|
+
| `paidPrice` | `Float` | no | Price paid (excl. tax) |
|
|
74
|
+
| `price` | `Float` | no | Unit price |
|
|
75
|
+
| `totalPrice` | `Float` | no | Line total |
|
|
76
|
+
| `taxPrice` | `Float` | no | Tax per unit |
|
|
77
|
+
| `totalTaxPrice` | `Float` | no | Line tax total |
|
|
78
|
+
| `taxType` | `String` | no | `GST`, `VAT`, or `EXCLTAX` |
|
|
79
|
+
| `currency` | `String` | no | ISO 4217 (e.g., `AUD`, `USD`, `EUR`) |
|
|
80
|
+
| `attributes` | `[AttributeInput]` | no | Item-level metadata |
|
|
81
|
+
|
|
82
|
+
### CreateFulfilmentChoiceWithOrderInput
|
|
83
|
+
|
|
84
|
+
| Field | Type | Required | Notes |
|
|
85
|
+
|-------|------|:--------:|-------|
|
|
86
|
+
| `deliveryType` | `String!` | YES | `STANDARD`, `EXPRESS`, `OVERNIGHT`, `3HOURS`, `NONE` (CC) |
|
|
87
|
+
| `ref` | `String` | no | Recommended — items link via `fulfilmentChoiceRef` |
|
|
88
|
+
| `type` | `String` | no | `HD`, `CC`, `SFS` (ship-from-store), or custom |
|
|
89
|
+
| `fulfilmentType` | `String` | no | Additional classification |
|
|
90
|
+
| `pickupLocationRef` | `String` | no | **Required for CC** — ref of the pickup store/location |
|
|
91
|
+
| `deliveryAddress` | `CreateCustomerAddressInput` | no | **Required for HD** — where to ship |
|
|
92
|
+
| `deliveryFirstName` | `String` | no | Recipient first name |
|
|
93
|
+
| `deliveryLastName` | `String` | no | Recipient last name |
|
|
94
|
+
| `deliveryContact` | `String` | no | Recipient phone |
|
|
95
|
+
| `deliveryEmail` | `String` | no | Recipient email |
|
|
96
|
+
| `deliverAfter` | `DateTime` | no | Earliest delivery window |
|
|
97
|
+
| `deliverBefore` | `DateTime` | no | Latest delivery window |
|
|
98
|
+
| `dispatchOn` | `DateTime` | no | Planned dispatch date |
|
|
99
|
+
| `currency` | `String` | no | ISO 4217 |
|
|
100
|
+
| `fulfilmentPrice` | `Float` | no | Shipping/collection fee |
|
|
101
|
+
| `fulfilmentTaxPrice` | `Float` | no | Tax on shipping fee |
|
|
102
|
+
| `deliveryInstruction` | `String` | no | Max 250 chars |
|
|
103
|
+
| `attributes` | `[AttributeInput]` | no | FC-level metadata (sourcing hints, consignment prefix, etc.) |
|
|
104
|
+
|
|
105
|
+
### CreateCustomerAddressInput (delivery address)
|
|
106
|
+
|
|
107
|
+
| Field | Type | Required | Notes |
|
|
108
|
+
|-------|------|:--------:|-------|
|
|
109
|
+
| `ref` | `String!` | YES | Unique address reference |
|
|
110
|
+
| `name` | `String` | no | Recipient name |
|
|
111
|
+
| `companyName` | `String` | no | Max 255 chars |
|
|
112
|
+
| `street` | `String` | no | Max 255 chars |
|
|
113
|
+
| `street2` | `String` | no | Max 255 chars |
|
|
114
|
+
| `city` | `String` | no | Max 255 chars |
|
|
115
|
+
| `state` | `String` | no | Max 200 chars |
|
|
116
|
+
| `postcode` | `String` | no | Max 100 chars |
|
|
117
|
+
| `region` | `String` | no | Max 250 chars |
|
|
118
|
+
| `country` | `String` | no | Max 100 chars (ISO 3166) |
|
|
119
|
+
| `latitude` | `Float` | no | |
|
|
120
|
+
| `longitude` | `Float` | no | |
|
|
121
|
+
| `timeZone` | `String` | no | Max 32 chars |
|
|
122
|
+
| `email` | `String` | no | Max 255 chars |
|
|
123
|
+
|
|
124
|
+
### CreateFinancialTransactionWithOrderInput
|
|
125
|
+
|
|
126
|
+
| Field | Type | Required | Notes |
|
|
127
|
+
|-------|------|:--------:|-------|
|
|
128
|
+
| `ref` | `String!` | YES | Unique transaction ref |
|
|
129
|
+
| `type` | `String!` | YES | Max 25 chars. e.g., `PAYMENT`, `REFUND` |
|
|
130
|
+
| `amount` | `Float!` | YES | Transaction amount |
|
|
131
|
+
| `currency` | `String!` | YES | ISO 4217 (3 chars) |
|
|
132
|
+
| `paymentMethod` | `String!` | YES | e.g., `CREDIT_CARD`, `PAYPAL`, `CASH` |
|
|
133
|
+
| `scale` | `Int` | no | Decimal places for unscaled value |
|
|
134
|
+
| `unscaledValue` | `Int` | no | Amount without decimals (for precision) |
|
|
135
|
+
| `externalTransactionCode` | `String` | no | External payment gateway code |
|
|
136
|
+
| `externalTransactionId` | `String` | no | External payment gateway ID |
|
|
137
|
+
| `cardType` | `String` | no | e.g., `VISA`, `MASTERCARD` |
|
|
138
|
+
| `paymentProvider` | `String` | no | e.g., `STRIPE`, `ADYEN` |
|
|
139
|
+
| `attributes` | `[AttributeInput]` | no | |
|
|
140
|
+
|
|
141
|
+
### AttributeInput
|
|
142
|
+
|
|
143
|
+
| Field | Type | Required | Notes |
|
|
144
|
+
|-------|------|:--------:|-------|
|
|
145
|
+
| `name` | `String!` | YES | Attribute key |
|
|
146
|
+
| `type` | `String!` | YES | `STRING`, `INTEGER`, `FLOAT`, `BOOLEAN`, `JSON` |
|
|
147
|
+
| `value` | `Json!` | YES | **Any valid JSON value** — see gotchas below |
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## The Item-to-FulfilmentChoice Link
|
|
152
|
+
|
|
153
|
+
This is the most misunderstood relationship in the createOrder mutation.
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
Order
|
|
157
|
+
|
|
|
158
|
+
+-- fulfilmentChoice (singular) OR fulfilmentChoices[] (plural)
|
|
159
|
+
| ref: "FC-HD-001" [{ ref: "FC-HD-001" }, { ref: "FC-CC-001" }]
|
|
160
|
+
|
|
|
161
|
+
+-- items[]
|
|
162
|
+
item[0].fulfilmentChoiceRef: "FC-HD-001" --> links to FC-HD-001
|
|
163
|
+
item[1].fulfilmentChoiceRef: "FC-CC-001" --> links to FC-CC-001
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Rules:**
|
|
167
|
+
1. Use `fulfilmentChoice` (singular) when ALL items share one fulfilment choice
|
|
168
|
+
2. Use `fulfilmentChoices` (plural) when items have DIFFERENT fulfilment choices
|
|
169
|
+
3. Each item's `fulfilmentChoiceRef` must match a `fulfilmentChoice.ref` — this is the join key
|
|
170
|
+
4. If using singular `fulfilmentChoice`, items can omit `fulfilmentChoiceRef` (auto-linked)
|
|
171
|
+
5. If using plural `fulfilmentChoices`, every item MUST have `fulfilmentChoiceRef`
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Input Contract vs Query Contract
|
|
176
|
+
|
|
177
|
+
The most common `ORDER::MULTI` mistake is mixing up the **mutation input join key** with the **query readback shape**.
|
|
178
|
+
|
|
179
|
+
### Input-side linkage (`createOrder`)
|
|
180
|
+
|
|
181
|
+
- Use `items[].fulfilmentChoiceRef` to link an order item to a fulfilment choice by `ref`
|
|
182
|
+
- Use `fulfilmentChoice` for one-FC orders and `fulfilmentChoices[]` for mixed or split orders
|
|
183
|
+
- Use `items[].productRef` and `items[].productCatalogueRef` on input
|
|
184
|
+
|
|
185
|
+
### Readback-side linkage (`orders` / `orderById`)
|
|
186
|
+
|
|
187
|
+
- Query `order.fulfilmentChoice { ... }` for the primary/singular FC
|
|
188
|
+
- Query `order.fulfilmentChoices { edges { node { ... } } }` for all FCs on mixed baskets
|
|
189
|
+
- Query `order.items { edges { node { fulfilmentChoice { ... } } } }` to verify which FC an item is linked to
|
|
190
|
+
- Query `order.items { edges { node { product { ... } } } }` to read the resolved product
|
|
191
|
+
- `Fulfilment.fulfilmentChoiceRef` remains queryable and is useful when tracing downstream fulfilment creation
|
|
192
|
+
|
|
193
|
+
**Important:** `fulfilmentChoiceRef` is a valid field on `CreateOrderItemWithOrderInput`, but on the validated readback schema `OrderItem` exposes `fulfilmentChoice { ... }`, not an `OrderItem.fulfilmentChoiceRef` scalar.
|
|
194
|
+
|
|
195
|
+
### Validated linkage query
|
|
196
|
+
|
|
197
|
+
```graphql
|
|
198
|
+
query orderLinkage($ref: [String!]) {
|
|
199
|
+
orders(ref: $ref, first: 1) {
|
|
200
|
+
edges {
|
|
201
|
+
node {
|
|
202
|
+
id
|
|
203
|
+
ref
|
|
204
|
+
type
|
|
205
|
+
status
|
|
206
|
+
fulfilmentChoice {
|
|
207
|
+
id
|
|
208
|
+
ref
|
|
209
|
+
type
|
|
210
|
+
status
|
|
211
|
+
deliveryType
|
|
212
|
+
}
|
|
213
|
+
fulfilmentChoices {
|
|
214
|
+
edges {
|
|
215
|
+
node {
|
|
216
|
+
id
|
|
217
|
+
ref
|
|
218
|
+
type
|
|
219
|
+
status
|
|
220
|
+
deliveryType
|
|
221
|
+
items {
|
|
222
|
+
edges {
|
|
223
|
+
node {
|
|
224
|
+
id
|
|
225
|
+
ref
|
|
226
|
+
quantity
|
|
227
|
+
order {
|
|
228
|
+
id
|
|
229
|
+
ref
|
|
230
|
+
}
|
|
231
|
+
fulfilmentChoice {
|
|
232
|
+
id
|
|
233
|
+
ref
|
|
234
|
+
type
|
|
235
|
+
status
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
fulfilments {
|
|
241
|
+
edges {
|
|
242
|
+
node {
|
|
243
|
+
id
|
|
244
|
+
ref
|
|
245
|
+
status
|
|
246
|
+
fulfilmentChoiceRef
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
items {
|
|
254
|
+
edges {
|
|
255
|
+
node {
|
|
256
|
+
id
|
|
257
|
+
ref
|
|
258
|
+
quantity
|
|
259
|
+
status
|
|
260
|
+
fulfilmentChoice {
|
|
261
|
+
id
|
|
262
|
+
ref
|
|
263
|
+
type
|
|
264
|
+
status
|
|
265
|
+
deliveryType
|
|
266
|
+
}
|
|
267
|
+
product {
|
|
268
|
+
id
|
|
269
|
+
ref
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Use this readback query after `createOrder` to prove the item-to-FC mapping actually materialized the way the mutation input intended.
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Pattern 1: Simple Home Delivery (HD)
|
|
285
|
+
|
|
286
|
+
Single fulfilment choice, all items shipped to one address.
|
|
287
|
+
|
|
288
|
+
```graphql
|
|
289
|
+
mutation createHDOrder($input: CreateOrderInput!) {
|
|
290
|
+
createOrder(input: $input) {
|
|
291
|
+
id ref status type
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
```json
|
|
297
|
+
{
|
|
298
|
+
"input": {
|
|
299
|
+
"ref": "ORD-HD-20260323-001",
|
|
300
|
+
"type": "HD",
|
|
301
|
+
"retailer": { "id": 123 },
|
|
302
|
+
"customer": { "id": 456 },
|
|
303
|
+
"items": [
|
|
304
|
+
{
|
|
305
|
+
"ref": "ORD-HD-20260323-001-ITEM-1",
|
|
306
|
+
"productRef": "PROD-SKU-001",
|
|
307
|
+
"productCatalogueRef": "MASTER_CATALOGUE",
|
|
308
|
+
"quantity": 2,
|
|
309
|
+
"paidPrice": 29.99,
|
|
310
|
+
"currency": "AUD",
|
|
311
|
+
"taxType": "GST",
|
|
312
|
+
"taxPrice": 2.73
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"ref": "ORD-HD-20260323-001-ITEM-2",
|
|
316
|
+
"productRef": "PROD-SKU-002",
|
|
317
|
+
"productCatalogueRef": "MASTER_CATALOGUE",
|
|
318
|
+
"quantity": 1,
|
|
319
|
+
"paidPrice": 49.99,
|
|
320
|
+
"currency": "AUD"
|
|
321
|
+
}
|
|
322
|
+
],
|
|
323
|
+
"fulfilmentChoice": {
|
|
324
|
+
"ref": "ORD-HD-20260323-001-FC",
|
|
325
|
+
"type": "HD",
|
|
326
|
+
"deliveryType": "STANDARD",
|
|
327
|
+
"deliveryAddress": {
|
|
328
|
+
"ref": "ORD-HD-20260323-001-ADDR",
|
|
329
|
+
"name": "Jane Smith",
|
|
330
|
+
"street": "42 Wallaby Way",
|
|
331
|
+
"city": "Sydney",
|
|
332
|
+
"state": "NSW",
|
|
333
|
+
"postcode": "2000",
|
|
334
|
+
"country": "AU"
|
|
335
|
+
},
|
|
336
|
+
"attributes": [
|
|
337
|
+
{ "name": "sourcingLocationRef", "type": "STRING", "value": "LOC-WH-001" },
|
|
338
|
+
{ "name": "consignmentPrefix", "type": "STRING", "value": "A_" }
|
|
339
|
+
]
|
|
340
|
+
},
|
|
341
|
+
"totalPrice": 109.97,
|
|
342
|
+
"totalTaxPrice": 10.00
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**When to use:** Standard ecommerce order. One delivery address, one or more items, all shipped together.
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## Pattern 2: Click & Collect (CC)
|
|
352
|
+
|
|
353
|
+
Customer picks up from a store. No delivery address — `pickupLocationRef` instead.
|
|
354
|
+
|
|
355
|
+
```json
|
|
356
|
+
{
|
|
357
|
+
"input": {
|
|
358
|
+
"ref": "ORD-CC-20260323-001",
|
|
359
|
+
"type": "CC",
|
|
360
|
+
"retailer": { "id": 123 },
|
|
361
|
+
"customer": { "id": 456 },
|
|
362
|
+
"items": [
|
|
363
|
+
{
|
|
364
|
+
"ref": "ORD-CC-20260323-001-ITEM-1",
|
|
365
|
+
"productRef": "PROD-SKU-001",
|
|
366
|
+
"productCatalogueRef": "MASTER_CATALOGUE",
|
|
367
|
+
"quantity": 1,
|
|
368
|
+
"paidPrice": 29.99
|
|
369
|
+
}
|
|
370
|
+
],
|
|
371
|
+
"fulfilmentChoice": {
|
|
372
|
+
"ref": "ORD-CC-20260323-001-FC",
|
|
373
|
+
"type": "CC",
|
|
374
|
+
"deliveryType": "NONE",
|
|
375
|
+
"pickupLocationRef": "LOC-STORE-001"
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**Key differences from HD:**
|
|
382
|
+
- `deliveryType`: `"NONE"` (customer picks up, no shipping)
|
|
383
|
+
- `pickupLocationRef`: ref of the store/location (must exist)
|
|
384
|
+
- No `deliveryAddress`
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Pattern 3: Split Shipment (MULTI with multiple FCs)
|
|
389
|
+
|
|
390
|
+
Items fulfilled from different sources. Uses `fulfilmentChoices` (plural) and each item links via `fulfilmentChoiceRef`.
|
|
391
|
+
|
|
392
|
+
```json
|
|
393
|
+
{
|
|
394
|
+
"input": {
|
|
395
|
+
"ref": "ORD-SPLIT-20260323-001",
|
|
396
|
+
"type": "MULTI",
|
|
397
|
+
"retailer": { "id": 123 },
|
|
398
|
+
"customer": { "id": 456 },
|
|
399
|
+
"items": [
|
|
400
|
+
{
|
|
401
|
+
"ref": "ORD-SPLIT-20260323-001-ITEM-1",
|
|
402
|
+
"productRef": "PROD-SKU-001",
|
|
403
|
+
"productCatalogueRef": "MASTER_CATALOGUE",
|
|
404
|
+
"quantity": 1,
|
|
405
|
+
"fulfilmentChoiceRef": "ORD-SPLIT-20260323-001-FC-HD"
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
"ref": "ORD-SPLIT-20260323-001-ITEM-2",
|
|
409
|
+
"productRef": "PROD-SKU-002",
|
|
410
|
+
"productCatalogueRef": "MASTER_CATALOGUE",
|
|
411
|
+
"quantity": 1,
|
|
412
|
+
"fulfilmentChoiceRef": "ORD-SPLIT-20260323-001-FC-HD"
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
"ref": "ORD-SPLIT-20260323-001-ITEM-3",
|
|
416
|
+
"productRef": "PROD-SKU-003",
|
|
417
|
+
"productCatalogueRef": "MASTER_CATALOGUE",
|
|
418
|
+
"quantity": 1,
|
|
419
|
+
"fulfilmentChoiceRef": "ORD-SPLIT-20260323-001-FC-CC"
|
|
420
|
+
}
|
|
421
|
+
],
|
|
422
|
+
"fulfilmentChoices": [
|
|
423
|
+
{
|
|
424
|
+
"ref": "ORD-SPLIT-20260323-001-FC-HD",
|
|
425
|
+
"type": "HD",
|
|
426
|
+
"deliveryType": "STANDARD",
|
|
427
|
+
"deliveryAddress": {
|
|
428
|
+
"ref": "ORD-SPLIT-20260323-001-ADDR",
|
|
429
|
+
"name": "Jane Smith",
|
|
430
|
+
"street": "42 Wallaby Way",
|
|
431
|
+
"city": "Sydney",
|
|
432
|
+
"postcode": "2000",
|
|
433
|
+
"country": "AU"
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
"ref": "ORD-SPLIT-20260323-001-FC-CC",
|
|
438
|
+
"type": "CC",
|
|
439
|
+
"deliveryType": "NONE",
|
|
440
|
+
"pickupLocationRef": "LOC-STORE-001"
|
|
441
|
+
}
|
|
442
|
+
]
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**Critical rules for split shipment:**
|
|
448
|
+
- Use `fulfilmentChoices` (plural array), NOT `fulfilmentChoice` (singular)
|
|
449
|
+
- Every item MUST have `fulfilmentChoiceRef` matching one of the FC refs
|
|
450
|
+
- Items 1 & 2 ship to home (HD), item 3 goes to store pickup (CC)
|
|
451
|
+
|
|
452
|
+
### Variant: Multi-location HD split within ORDER::MULTI
|
|
453
|
+
|
|
454
|
+
`ORDER::MULTI` does **not** mean each fulfilment choice must have a different FC `type`. A common pattern is one mixed-basket order with multiple `HD` fulfilment choices, each routed to a different sourcing location.
|
|
455
|
+
|
|
456
|
+
```json
|
|
457
|
+
{
|
|
458
|
+
"input": {
|
|
459
|
+
"ref": "ORD-MULTI-HD-20260323-001",
|
|
460
|
+
"type": "MULTI",
|
|
461
|
+
"retailer": { "id": "123" },
|
|
462
|
+
"customer": { "id": "456" },
|
|
463
|
+
"items": [
|
|
464
|
+
{
|
|
465
|
+
"ref": "ORD-MULTI-HD-20260323-001-ITEM-1",
|
|
466
|
+
"productRef": "PROD-SKU-001",
|
|
467
|
+
"productCatalogueRef": "MASTER_CATALOGUE",
|
|
468
|
+
"quantity": 1,
|
|
469
|
+
"fulfilmentChoiceRef": "ORD-MULTI-HD-20260323-001-FC-HD-WH1"
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
"ref": "ORD-MULTI-HD-20260323-001-ITEM-2",
|
|
473
|
+
"productRef": "PROD-SKU-002",
|
|
474
|
+
"productCatalogueRef": "MASTER_CATALOGUE",
|
|
475
|
+
"quantity": 1,
|
|
476
|
+
"fulfilmentChoiceRef": "ORD-MULTI-HD-20260323-001-FC-HD-WH2"
|
|
477
|
+
}
|
|
478
|
+
],
|
|
479
|
+
"fulfilmentChoices": [
|
|
480
|
+
{
|
|
481
|
+
"ref": "ORD-MULTI-HD-20260323-001-FC-HD-WH1",
|
|
482
|
+
"type": "HD",
|
|
483
|
+
"deliveryType": "STANDARD",
|
|
484
|
+
"deliveryAddress": {
|
|
485
|
+
"ref": "ORD-MULTI-HD-20260323-001-ADDR-1",
|
|
486
|
+
"name": "Jane Smith",
|
|
487
|
+
"street": "42 Wallaby Way",
|
|
488
|
+
"city": "Sydney",
|
|
489
|
+
"postcode": "2000",
|
|
490
|
+
"country": "AU"
|
|
491
|
+
},
|
|
492
|
+
"attributes": [
|
|
493
|
+
{ "name": "sourcingLocationRef", "type": "STRING", "value": "LOC-WH-001" }
|
|
494
|
+
]
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
"ref": "ORD-MULTI-HD-20260323-001-FC-HD-WH2",
|
|
498
|
+
"type": "HD",
|
|
499
|
+
"deliveryType": "STANDARD",
|
|
500
|
+
"deliveryAddress": {
|
|
501
|
+
"ref": "ORD-MULTI-HD-20260323-001-ADDR-2",
|
|
502
|
+
"name": "Jane Smith",
|
|
503
|
+
"street": "42 Wallaby Way",
|
|
504
|
+
"city": "Sydney",
|
|
505
|
+
"postcode": "2000",
|
|
506
|
+
"country": "AU"
|
|
507
|
+
},
|
|
508
|
+
"attributes": [
|
|
509
|
+
{ "name": "sourcingLocationRef", "type": "STRING", "value": "LOC-WH-002" }
|
|
510
|
+
]
|
|
511
|
+
}
|
|
512
|
+
]
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
**Why this matters:**
|
|
518
|
+
- The order `type` stays `MULTI`
|
|
519
|
+
- Each FC can still be `HD`, `CC`, `SFS`, or another implementation-specific type
|
|
520
|
+
- The split is expressed by multiple FC refs plus item-level `fulfilmentChoiceRef`
|
|
521
|
+
- Source-specific routing belongs on the FC, commonly via attributes such as `sourcingLocationRef`
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
## Pattern 4: Order with Financial Transactions
|
|
526
|
+
|
|
527
|
+
Include payment records inline with the order.
|
|
528
|
+
|
|
529
|
+
```json
|
|
530
|
+
{
|
|
531
|
+
"input": {
|
|
532
|
+
"ref": "ORD-PAY-20260323-001",
|
|
533
|
+
"type": "HD",
|
|
534
|
+
"retailer": { "id": 123 },
|
|
535
|
+
"customer": { "id": 456 },
|
|
536
|
+
"items": [
|
|
537
|
+
{
|
|
538
|
+
"ref": "ORD-PAY-20260323-001-ITEM-1",
|
|
539
|
+
"productRef": "PROD-SKU-001",
|
|
540
|
+
"productCatalogueRef": "MASTER_CATALOGUE",
|
|
541
|
+
"quantity": 1,
|
|
542
|
+
"paidPrice": 99.99
|
|
543
|
+
}
|
|
544
|
+
],
|
|
545
|
+
"fulfilmentChoice": {
|
|
546
|
+
"ref": "ORD-PAY-20260323-001-FC",
|
|
547
|
+
"type": "HD",
|
|
548
|
+
"deliveryType": "EXPRESS",
|
|
549
|
+
"fulfilmentPrice": 12.00,
|
|
550
|
+
"deliveryAddress": {
|
|
551
|
+
"ref": "ORD-PAY-20260323-001-ADDR",
|
|
552
|
+
"street": "100 George St",
|
|
553
|
+
"city": "Sydney",
|
|
554
|
+
"postcode": "2000",
|
|
555
|
+
"country": "AU"
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
"totalPrice": 111.99,
|
|
559
|
+
"financialTransactions": [
|
|
560
|
+
{
|
|
561
|
+
"ref": "ORD-PAY-20260323-001-FT-1",
|
|
562
|
+
"type": "PAYMENT",
|
|
563
|
+
"amount": 111.99,
|
|
564
|
+
"currency": "AUD",
|
|
565
|
+
"paymentMethod": "CREDIT_CARD",
|
|
566
|
+
"cardType": "VISA",
|
|
567
|
+
"paymentProvider": "STRIPE",
|
|
568
|
+
"externalTransactionId": "pi_3abc123"
|
|
569
|
+
}
|
|
570
|
+
],
|
|
571
|
+
"billingAddress": {
|
|
572
|
+
"name": "Jane Smith",
|
|
573
|
+
"street": "100 George St",
|
|
574
|
+
"city": "Sydney",
|
|
575
|
+
"postcode": "2000",
|
|
576
|
+
"country": "AU"
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
## Pattern 5: Minimal Order (fewest possible fields)
|
|
585
|
+
|
|
586
|
+
Absolute minimum required fields. Useful for smoke tests.
|
|
587
|
+
|
|
588
|
+
```json
|
|
589
|
+
{
|
|
590
|
+
"input": {
|
|
591
|
+
"ref": "ORD-MIN-20260323-001",
|
|
592
|
+
"type": "HD",
|
|
593
|
+
"retailer": { "id": 123 },
|
|
594
|
+
"customer": { "id": 456 },
|
|
595
|
+
"items": [
|
|
596
|
+
{
|
|
597
|
+
"ref": "ORD-MIN-20260323-001-ITEM-1",
|
|
598
|
+
"productRef": "PROD-SKU-001",
|
|
599
|
+
"quantity": 1
|
|
600
|
+
}
|
|
601
|
+
],
|
|
602
|
+
"fulfilmentChoice": {
|
|
603
|
+
"deliveryType": "STANDARD"
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
Note: `fulfilmentChoice.ref`, `productCatalogueRef`, and `deliveryAddress` are all optional. This creates a valid order but with minimal data — workflows may require attributes that aren't present.
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
## Return Fields
|
|
614
|
+
|
|
615
|
+
**Input field names differ from return field names.** The mutation input uses `amount` but the return type uses `total`. The input uses `productRef` but the return type uses `product { ... on VariantProduct { ref } }`.
|
|
616
|
+
|
|
617
|
+
### Validated return field selection
|
|
618
|
+
|
|
619
|
+
```graphql
|
|
620
|
+
mutation createOrder($input: CreateOrderInput!) {
|
|
621
|
+
createOrder(input: $input) {
|
|
622
|
+
# Order fields
|
|
623
|
+
id ref status type totalPrice totalTaxPrice
|
|
624
|
+
|
|
625
|
+
# Items — product is a UNION type, needs inline fragment
|
|
626
|
+
items {
|
|
627
|
+
edges {
|
|
628
|
+
node {
|
|
629
|
+
id ref quantity status
|
|
630
|
+
product { ... on VariantProduct { ref name } }
|
|
631
|
+
fulfilmentChoice { id ref type }
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
# Singular FC return
|
|
637
|
+
fulfilmentChoice {
|
|
638
|
+
id ref type deliveryType
|
|
639
|
+
deliveryAddress { ref name street city postcode country }
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
# Plural FC return (for split orders)
|
|
643
|
+
fulfilmentChoices {
|
|
644
|
+
edges {
|
|
645
|
+
node { id ref type deliveryType pickupLocationRef }
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
# Financial
|
|
650
|
+
financialTransactions {
|
|
651
|
+
edges {
|
|
652
|
+
node { id ref type total currency paymentMethod status }
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
# Billing
|
|
657
|
+
billingAddress { name street city postcode country }
|
|
658
|
+
|
|
659
|
+
# Related entities
|
|
660
|
+
customer { id firstName lastName primaryEmail }
|
|
661
|
+
retailer { id tradingName }
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
### Input vs Return field name differences
|
|
667
|
+
|
|
668
|
+
| Concept | Input field name | Return field name |
|
|
669
|
+
|---------|-----------------|-------------------|
|
|
670
|
+
| Product reference | `items[].productRef` (string) | `items[].product { ... on VariantProduct { ref } }` (union) |
|
|
671
|
+
| Transaction amount | `financialTransactions[].amount` (Float) | `financialTransactions[].total` (Float) |
|
|
672
|
+
| Fulfilment choices | `fulfilmentChoices[]` (flat array input) | `fulfilmentChoices { edges { node { ... } } }` (Relay connection) |
|
|
673
|
+
| Items | `items[]` (flat array input) | `items { edges { node { ... } } }` (Relay connection) |
|
|
674
|
+
|
|
675
|
+
---
|
|
676
|
+
|
|
677
|
+
## Gotchas
|
|
678
|
+
|
|
679
|
+
### 1. RetailerId and CustomerId are wrapper objects
|
|
680
|
+
|
|
681
|
+
```json
|
|
682
|
+
// WRONG — bare integer
|
|
683
|
+
"retailer": 123,
|
|
684
|
+
"customer": 456
|
|
685
|
+
|
|
686
|
+
// WRONG — string ref
|
|
687
|
+
"retailer": "RET-001",
|
|
688
|
+
"customer": "CUST-001"
|
|
689
|
+
|
|
690
|
+
// CORRECT — wrapper object with numeric ID
|
|
691
|
+
"retailer": { "id": 123 },
|
|
692
|
+
"customer": { "id": 456 }
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
### 2. AttributeInput.value is `Json!` scalar
|
|
696
|
+
|
|
697
|
+
The `value` field accepts **any valid JSON value** — string, number, boolean, object, array. When passing via GraphQL variables (JSON), just use the native JSON type:
|
|
698
|
+
|
|
699
|
+
```json
|
|
700
|
+
{ "name": "colour", "type": "STRING", "value": "red" }
|
|
701
|
+
{ "name": "weight", "type": "FLOAT", "value": 2.5 }
|
|
702
|
+
{ "name": "express", "type": "BOOLEAN", "value": true }
|
|
703
|
+
{ "name": "tags", "type": "JSON", "value": ["fragile", "oversized"] }
|
|
704
|
+
{ "name": "metadata", "type": "JSON", "value": { "source": "web", "channel": "AU" } }
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
### 3. fulfilmentChoice (singular) vs fulfilmentChoices (plural)
|
|
708
|
+
|
|
709
|
+
| Scenario | Use | fulfilmentChoiceRef on items? |
|
|
710
|
+
|----------|-----|:----------------------------:|
|
|
711
|
+
| All items → one delivery | `fulfilmentChoice` (singular) | Optional (auto-linked) |
|
|
712
|
+
| Items → different deliveries | `fulfilmentChoices` (plural array) | **Required on every item** |
|
|
713
|
+
| Mixed HD + CC | `fulfilmentChoices` (plural array) | **Required on every item** |
|
|
714
|
+
|
|
715
|
+
Never use both `fulfilmentChoice` and `fulfilmentChoices` on the same order.
|
|
716
|
+
|
|
717
|
+
### 4. Item refs must be unique within the order
|
|
718
|
+
|
|
719
|
+
```json
|
|
720
|
+
// WRONG — duplicate refs
|
|
721
|
+
{ "ref": "ITEM-1", "productRef": "SKU-A", "quantity": 1 },
|
|
722
|
+
{ "ref": "ITEM-1", "productRef": "SKU-B", "quantity": 1 }
|
|
723
|
+
|
|
724
|
+
// CORRECT — unique refs
|
|
725
|
+
{ "ref": "ORD-001-ITEM-1", "productRef": "SKU-A", "quantity": 1 },
|
|
726
|
+
{ "ref": "ORD-001-ITEM-2", "productRef": "SKU-B", "quantity": 1 }
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
**Best practice:** Prefix item refs with the order ref to guarantee uniqueness.
|
|
730
|
+
|
|
731
|
+
### 5. deliveryAddress.ref is required when providing a delivery address
|
|
732
|
+
|
|
733
|
+
```json
|
|
734
|
+
// WRONG — ref missing
|
|
735
|
+
"deliveryAddress": { "street": "42 Wallaby Way", "city": "Sydney" }
|
|
736
|
+
|
|
737
|
+
// CORRECT
|
|
738
|
+
"deliveryAddress": { "ref": "ORD-001-ADDR", "street": "42 Wallaby Way", "city": "Sydney" }
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### 6. productCatalogueRef should always be provided
|
|
742
|
+
|
|
743
|
+
While technically optional (falls back to compatibility catalogue), omitting it is error-prone. Always pair `productRef` with `productCatalogueRef` to ensure the product is found in the correct catalogue.
|
|
744
|
+
|
|
745
|
+
### 7. The order type drives workflow routing
|
|
746
|
+
|
|
747
|
+
The `type` field (e.g., `HD`, `CC`, `MULTI`) determines which workflow processes the order. The Orchestration Engine matches `type` against workflow trigger conditions. Using an unrecognized type means no workflow picks it up.
|
|
748
|
+
|
|
749
|
+
Common types:
|
|
750
|
+
- `HD` — Home Delivery
|
|
751
|
+
- `CC` — Click & Collect
|
|
752
|
+
- `MULTI` — Mixed fulfilment (split shipment)
|
|
753
|
+
- Custom types defined by the implementation
|
|
754
|
+
|
|
755
|
+
### 8. DateTime format
|
|
756
|
+
|
|
757
|
+
`deliverAfter`, `deliverBefore`, `dispatchOn` use ISO 8601: `"2026-03-23T10:00:00.000Z"`
|
|
758
|
+
|
|
759
|
+
### 9. Relay connections on return but flat arrays on input
|
|
760
|
+
|
|
761
|
+
Input takes flat JSON arrays (`items: [...]`). Return fields use Relay connections (`items { edges { node { ... } } }`). Don't confuse the two.
|
|
762
|
+
|
|
763
|
+
---
|
|
764
|
+
|
|
765
|
+
## Discovery Queries (prerequisites)
|
|
766
|
+
|
|
767
|
+
Before calling `createOrder`, discover the required IDs and refs:
|
|
768
|
+
|
|
769
|
+
```graphql
|
|
770
|
+
# Retailer ID
|
|
771
|
+
query { retailers(first: 1) { edges { node { id ref tradingName } } } }
|
|
772
|
+
|
|
773
|
+
# Customer ID
|
|
774
|
+
query { customers(first: 1) { edges { node { id firstName lastName primaryEmail } } } }
|
|
775
|
+
|
|
776
|
+
# Product refs + catalogue
|
|
777
|
+
query { variantProducts(first: 5, catalogue: { ref: "MASTER_CATALOGUE" }) {
|
|
778
|
+
edges { node { ref name status catalogue { ref } } }
|
|
779
|
+
} }
|
|
780
|
+
|
|
781
|
+
# Location refs (for CC pickup or sourcing)
|
|
782
|
+
query { locations(first: 10, type: "STORE", status: "ACTIVE") {
|
|
783
|
+
edges { node { id ref name type status } }
|
|
784
|
+
} }
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
---
|
|
788
|
+
|
|
789
|
+
## Workflow Side Effects
|
|
790
|
+
|
|
791
|
+
Calling `createOrder` automatically fires a `CREATE` event on the new Order entity. The Orchestration Engine then:
|
|
792
|
+
|
|
793
|
+
1. Matches the event to an ORDER workflow (by `type` or catch-all)
|
|
794
|
+
2. Executes the `CREATED` status ruleset
|
|
795
|
+
3. Typically triggers sourcing, fulfilment creation, inventory reservation
|
|
796
|
+
|
|
797
|
+
No manual event dispatch is needed after `createOrder` — the workflow starts automatically.
|