@cxtms/cx-schema 1.9.67 → 1.9.241
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/dist/cli.js +43 -9
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/schemas/actions/all.json +5 -1
- package/schemas/actions/reload.json +17 -0
- package/schemas/actions/setSessionStorage.json +38 -0
- package/schemas/actions/sound.json +39 -0
- package/schemas/actions/vibrate.json +48 -0
- package/schemas/components/avatar.json +1 -0
- package/schemas/components/badge.json +79 -0
- package/schemas/components/barcodeScanner.json +28 -0
- package/schemas/components/camera.json +177 -0
- package/schemas/components/card.json +78 -0
- package/schemas/components/dashboard-widget.json +39 -0
- package/schemas/components/dashboard.json +46 -0
- package/schemas/components/dataGrid.json +15 -0
- package/schemas/components/field-collection.json +86 -2
- package/schemas/components/field.json +39 -4
- package/schemas/components/form.json +4 -0
- package/schemas/components/index.json +17 -1
- package/schemas/components/infoLine.json +1 -0
- package/schemas/components/map.json +167 -0
- package/schemas/components/module.json +32 -0
- package/schemas/components/planner.json +1 -0
- package/schemas/components/progressBar.json +1 -0
- package/schemas/components/timeline.json +216 -74
- package/schemas/components/timelineGrid.json +5 -1
- package/schemas/fields/autocomplete-googleplaces.json +2 -2
- package/schemas/fields/datetime.json +2 -2
- package/schemas/fields/index.json +4 -0
- package/schemas/fields/number-select.json +88 -0
- package/schemas/fields/rangedatetime.json +10 -2
- package/schemas/fields/select-async.json +2 -2
- package/schemas/fields/toggle.json +91 -0
- package/schemas/schema.graphql +11016 -12171
- package/schemas/schemas.json +131 -4
- package/schemas/workflows/flow/entity.json +3 -1
- package/schemas/workflows/input.json +1 -1
- package/schemas/workflows/output.json +1 -1
- package/schemas/workflows/tasks/all.json +9 -0
- package/schemas/workflows/tasks/commodity.json +8 -0
- package/schemas/workflows/tasks/contact-address.json +25 -0
- package/schemas/workflows/tasks/dispatch-routes.json +64 -0
- package/schemas/workflows/tasks/edi.json +15 -0
- package/schemas/workflows/tasks/export.json +21 -0
- package/schemas/workflows/tasks/httpRequest.json +4 -0
- package/schemas/workflows/tasks/import.json +5 -0
- package/schemas/workflows/tasks/order-move.json +38 -0
- package/schemas/workflows/tasks/order.json +6 -0
- package/schemas/workflows/tasks/organization-config.json +101 -0
- package/schemas/workflows/tasks/tracking-event.json +11 -3
- package/schemas/workflows/tasks/workflow-execute.json +4 -0
- package/schemas/workflows/variable.json +2 -2
- package/schemas/workflows/workflow.json +39 -2
- package/skills/cxtms-developer/SKILL.md +5 -2
- package/skills/cxtms-developer/ref-cli-auth.md +2 -0
- package/skills/cxtms-developer/ref-entity-commodity.md +41 -5
- package/skills/cxtms-developer/ref-entity-contact.md +46 -0
- package/skills/cxtms-developer/ref-entity-dispatch-routing.md +195 -0
- package/skills/cxtms-developer/ref-entity-geography.md +26 -1
- package/skills/cxtms-developer/ref-entity-job.md +6 -2
- package/skills/cxtms-developer/ref-entity-order-sub.md +1 -1
- package/skills/cxtms-developer/ref-entity-order.md +36 -3
- package/skills/cxtms-developer/ref-entity-organization.md +4 -2
- package/skills/cxtms-developer/ref-entity-shared.md +79 -3
- package/skills/cxtms-developer/ref-graphql-query.md +246 -4
- package/skills/cxtms-module-builder/SKILL.md +33 -6
- package/skills/cxtms-module-builder/ref-components-data.md +85 -0
- package/skills/cxtms-module-builder/ref-components-display.md +282 -8
- package/skills/cxtms-module-builder/ref-components-forms.md +386 -21
- package/skills/cxtms-module-builder/ref-components-interactive.md +2 -0
- package/skills/cxtms-module-builder/ref-components-layout.md +288 -29
- package/skills/cxtms-module-builder/ref-components-specialized.md +168 -20
- package/skills/cxtms-workflow-builder/SKILL.md +45 -6
- package/skills/cxtms-workflow-builder/ref-entity.md +100 -4
- package/skills/cxtms-workflow-builder/ref-expressions-ncalc.md +35 -2
- package/skills/cxtms-workflow-builder/ref-expressions-template.md +15 -1
- package/skills/cxtms-workflow-builder/ref-flow.md +6 -1
- package/skills/cxtms-workflow-builder/ref-query.md +16 -0
- package/skills/cxtms-workflow-builder/ref-utilities.md +32 -2
- package/templates/workflow-public-api.yaml +1 -0
|
@@ -50,6 +50,10 @@
|
|
|
50
50
|
"description": "Inputs to pass to the child workflow",
|
|
51
51
|
"additionalProperties": true
|
|
52
52
|
},
|
|
53
|
+
"executionId": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"description": "Optional caller-provided GUID idempotency key for retry-safe workflow execution"
|
|
56
|
+
},
|
|
53
57
|
"async": {
|
|
54
58
|
"type": "boolean",
|
|
55
59
|
"description": "Execute asynchronously"
|
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
},
|
|
32
32
|
"key": {
|
|
33
33
|
"type": "string",
|
|
34
|
-
"description": "Optional key within configuration"
|
|
34
|
+
"description": "Optional key within configuration. If omitted, the variable receives the whole config object with organizationConfigId injected alongside custom values."
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"required": ["configName"],
|
|
38
38
|
"additionalProperties": true
|
|
39
39
|
}
|
|
40
40
|
],
|
|
41
|
-
"description": "Load value from configuration"
|
|
41
|
+
"description": "Load value from organization configuration. When a full config is loaded without a key, the runtime injects organizationConfigId into the returned object; keyed lookups return only the keyed value."
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"required": ["name"],
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
},
|
|
68
68
|
"workflowType": {
|
|
69
69
|
"type": "string",
|
|
70
|
-
"enum": ["Document", "Quote", "Flow", "Webhook", "PublicApi"],
|
|
71
|
-
"description": "Workflow type
|
|
70
|
+
"enum": ["Document", "Quote", "Flow", "Webhook", "PublicApi", "McpTool", "McpResource", "McpPrompt"],
|
|
71
|
+
"description": "Workflow type, including organization-scoped MCP tools, resources, and prompts. Omit for standard process workflows."
|
|
72
72
|
},
|
|
73
73
|
"runAs": {
|
|
74
74
|
"type": "string",
|
|
@@ -266,6 +266,19 @@
|
|
|
266
266
|
"required": ["path", "method"],
|
|
267
267
|
"additionalProperties": true
|
|
268
268
|
},
|
|
269
|
+
"mcp": {
|
|
270
|
+
"type": "object",
|
|
271
|
+
"description": "MCP metadata for McpTool, McpResource, and McpPrompt workflows",
|
|
272
|
+
"properties": {
|
|
273
|
+
"name": { "type": "string", "pattern": "^[a-zA-Z0-9_-]{1,64}$" },
|
|
274
|
+
"title": { "type": "string" },
|
|
275
|
+
"description": { "type": "string" },
|
|
276
|
+
"uri": { "type": "string", "format": "uri" },
|
|
277
|
+
"mimeType": { "type": "string" },
|
|
278
|
+
"timeout": { "type": "integer", "minimum": 0, "default": 60 }
|
|
279
|
+
},
|
|
280
|
+
"additionalProperties": false
|
|
281
|
+
},
|
|
269
282
|
"entity": {
|
|
270
283
|
"$ref": "flow/entity.json",
|
|
271
284
|
"description": "Entity configuration for Flow workflows"
|
|
@@ -329,6 +342,30 @@
|
|
|
329
342
|
"then": {
|
|
330
343
|
"required": ["workflow", "activities", "api"]
|
|
331
344
|
}
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"if": {
|
|
348
|
+
"properties": { "workflow": { "properties": { "workflowType": { "enum": ["McpTool", "McpPrompt"] } }, "required": ["workflowType"] } }
|
|
349
|
+
},
|
|
350
|
+
"then": {
|
|
351
|
+
"required": ["workflow", "activities", "mcp"],
|
|
352
|
+
"properties": {
|
|
353
|
+
"mcp": { "required": ["name"] },
|
|
354
|
+
"workflow": { "properties": { "executionMode": { "const": "Sync" } } }
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"if": {
|
|
360
|
+
"properties": { "workflow": { "properties": { "workflowType": { "const": "McpResource" } }, "required": ["workflowType"] } }
|
|
361
|
+
},
|
|
362
|
+
"then": {
|
|
363
|
+
"required": ["workflow", "activities", "mcp"],
|
|
364
|
+
"properties": {
|
|
365
|
+
"mcp": { "required": ["uri"] },
|
|
366
|
+
"workflow": { "properties": { "executionMode": { "const": "Sync" } } }
|
|
367
|
+
}
|
|
368
|
+
}
|
|
332
369
|
}
|
|
333
370
|
],
|
|
334
371
|
"additionalProperties": true
|
|
@@ -39,6 +39,7 @@ Use `--feature <feature_name>` with `cx-cli create` to automatically place files
|
|
|
39
39
|
|
|
40
40
|
!cat skills/cxtms-developer/ref-entity-order-sub.md
|
|
41
41
|
!cat skills/cxtms-developer/ref-entity-job.md
|
|
42
|
+
!cat skills/cxtms-developer/ref-entity-dispatch-routing.md
|
|
42
43
|
|
|
43
44
|
### Pricing & Accounting Lookups
|
|
44
45
|
|
|
@@ -66,8 +67,10 @@ Use `--feature <feature_name>` with `cx-cli create` to automatically place files
|
|
|
66
67
|
| **Primary** | Order, Contact, Commodity, AccountingTransaction | ref-entity-order/contact/commodity/accounting.md |
|
|
67
68
|
| **Order sub** | OrderEntity, TrackingEvent, EventDefinition, LinkedOrder, OrderDocument | ref-entity-order-sub.md |
|
|
68
69
|
| **Job** | Job, JobOrder, JobStatus | ref-entity-job.md |
|
|
70
|
+
| **Dispatch routing** | DispatchRouteStatus, DispatchRouteTemplate, DispatchRoute, stops | ref-entity-dispatch-routing.md |
|
|
71
|
+
| **Order moves** | OrderMove, OrderMoveLeg, move and leg statuses | ref-entity-dispatch-routing.md |
|
|
69
72
|
| **Pricing** | Rate, Lane, Discount, AccountingItem, AccountingAccount, PaymentTerm | ref-entity-rate.md |
|
|
70
|
-
| **Shared** | Tag, Attachment, Division, EquipmentType, PackageType, Note/NoteThread | ref-entity-shared.md |
|
|
73
|
+
| **Shared** | Tag, Attachment, Division, EquipmentType, Equipment, EquipmentStatus, PackageType, Note/NoteThread | ref-entity-shared.md |
|
|
71
74
|
| **Geography** | Country, State, City, Port, Vessel, CustomCode, ModeOfTransportation | ref-entity-geography.md |
|
|
72
75
|
| **Equipment** | Equipment, EquipmentStatus | ref-entity-equipment.md |
|
|
73
76
|
| **Warehouse** | InventoryItem, WarehouseLocation, CargoMovement (Order variant) | ref-entity-warehouse.md |
|
|
@@ -90,7 +93,7 @@ Most entities have `customValues` — a `Dictionary<string, object?>` stored as
|
|
|
90
93
|
|
|
91
94
|
**Sub-entities**: OrderEntity, OrderCommodity, ContactAddress, ContactPaymentMethod, CommodityType, CommodityTag, OrderTag, LinkedOrder, InventoryItemTag
|
|
92
95
|
|
|
93
|
-
**Lookup entities**: Job, JobStatus, Tag, Attachment, EventDefinition, Rate, Lane, Discount, AccountingItem, Port, Country, State, City, ModeOfTransportation
|
|
96
|
+
**Lookup entities**: Job, JobStatus, DispatchRouteStatus, DispatchRouteTemplate, DispatchRoute, DispatchRouteStop, DispatchRouteTemplateStop, Tag, Attachment, EventDefinition, Rate, Lane, Discount, AccountingItem, Port, Country, State, City, ModeOfTransportation
|
|
94
97
|
|
|
95
98
|
**Fleet**: Equipment (replace semantics — null clears, non-null full-replaces)
|
|
96
99
|
|
|
@@ -86,6 +86,8 @@ npx cxtms deploy-all --org 42
|
|
|
86
86
|
|
|
87
87
|
Validates all YAML files first, then pushes modules and workflows to the server. Skips files with validation errors and reports results.
|
|
88
88
|
|
|
89
|
+
Deploying a module whose matching server record was soft-deleted restores and updates that record. The server resolves deleted modules by module and organization ID, avoiding a duplicate-key insert during reinstall.
|
|
90
|
+
|
|
89
91
|
> Note: `deploy-all` pushes local YAML files to the CX server (per-file `updateWorkflow` / `updateAppModule` mutations). It does **not** touch git or create a PR. Use `app release` to open a git PR from the server's pending changes.
|
|
90
92
|
|
|
91
93
|
## App Manifest Management
|
|
@@ -116,11 +116,14 @@ Field names as used in workflow expressions: `{{ entity.description }}`, `{{ ent
|
|
|
116
116
|
| `getChildCommodities(filter)` | `[Commodity]` | Child commodities |
|
|
117
117
|
| `getParentCommodity` | `Commodity` | Parent commodity |
|
|
118
118
|
| `getRelatedOrders(filter)` | `[Order]` | Related orders |
|
|
119
|
-
| `getRelatedOrder(filter)` | `Order` | First related order |
|
|
119
|
+
| `getRelatedOrder(filter)` | `Order` | First non-draft related order, batched across commodity rows; optional order filter; lowest `orderId` wins when multiple orders match |
|
|
120
|
+
| `getRelatedDispatchRoutes(filter?, orderBy?)` | `[DispatchRoute]` | Dispatch routes linked through non-draft orders attached to this commodity or any descendant commodity |
|
|
120
121
|
| `getCommodityTrackingNumber(idPropertyName)` | `TrackingNumber` | Lookup |
|
|
121
122
|
| `getCommodityAttachments(filter)` | `[Attachment]` | |
|
|
122
|
-
| `
|
|
123
|
+
| `getWeightTotal(weightUnit)` | `decimal?` | Total weight converted to requested unit; defaults to commodity `weightUnit` when omitted |
|
|
124
|
+
| `lastTrackingEvent(eventDefinitionName, orderBy?)` | `TrackingEvent` | Most recent (or earliest) tracking event, resolved via batched DataLoader. `eventDefinitionName` may contain pipe-separated names such as `Departed|Delivered`; the resolver selects the winner across all matching definitions. `orderBy` is **honoured**: omit or prefix with `-` for DESC (latest event: `COALESCE(EventDate, Created) DESC, TrackingEventId DESC`); no prefix for ASC (earliest event: same columns ASC). Default is DESC. |
|
|
123
125
|
| `changeHistory(startDate, endDate, maxResults)` | `[ChangeHistory]` | Audit trail |
|
|
126
|
+
| `getContact(idPropertyName)` | `Contact` | Resolves a contact ID stored in `customValues[idPropertyName]` within the same organization; returns null when no valid ID exists |
|
|
124
127
|
|
|
125
128
|
## Container/Child Pattern (Self-Referencing)
|
|
126
129
|
|
|
@@ -139,6 +142,7 @@ Container Commodity (parent)
|
|
|
139
142
|
- Changing warehouse location on parent cascades to children
|
|
140
143
|
- Changing commodity status on parent cascades to children
|
|
141
144
|
- Deleting parent cascades to children
|
|
145
|
+
- `updateCommodity` can optionally cascade `customValues` to direct children (`cascadeToChildren`) or promote markers to the parent once all direct siblings have those keys (`promoteParentWhenAllChildren`)
|
|
142
146
|
|
|
143
147
|
## CommodityTrackingNumber Sub-Entity
|
|
144
148
|
|
|
@@ -206,9 +210,37 @@ conditions:
|
|
|
206
210
|
# Update via Commodity/Update task
|
|
207
211
|
inputs:
|
|
208
212
|
commodityId: "{{ entity.commodityId }}"
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
213
|
+
entity:
|
|
214
|
+
customValues:
|
|
215
|
+
lotNumber: "LOT-001"
|
|
216
|
+
hazmat: true
|
|
217
|
+
|
|
218
|
+
# Pallet/carton scan cascade: parent scan marks direct children
|
|
219
|
+
inputs:
|
|
220
|
+
commodityId: "{{ pallet.commodityId }}"
|
|
221
|
+
cascadeToChildren: true
|
|
222
|
+
entity:
|
|
223
|
+
customValues:
|
|
224
|
+
scanned: true
|
|
225
|
+
|
|
226
|
+
# Carton scan promotion: final marked sibling marks the parent
|
|
227
|
+
inputs:
|
|
228
|
+
commodityId: "{{ carton.commodityId }}"
|
|
229
|
+
promoteParentWhenAllChildren: true
|
|
230
|
+
entity:
|
|
231
|
+
customValues:
|
|
232
|
+
scanned: true
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Resolve custom-value contact references:
|
|
236
|
+
|
|
237
|
+
```graphql
|
|
238
|
+
getCommodities(organizationId: 1, take: 1) {
|
|
239
|
+
items {
|
|
240
|
+
commodityId
|
|
241
|
+
getContact(idPropertyName: "shipperContactId") { contactId name }
|
|
242
|
+
}
|
|
243
|
+
}
|
|
212
244
|
```
|
|
213
245
|
|
|
214
246
|
**Entities with own customValues:**
|
|
@@ -238,3 +270,7 @@ aggregations:
|
|
|
238
270
|
parameter: "eventCode"
|
|
239
271
|
expression: "any([Commodity.CommodityEvents], [each.TrackingEvent.EventDefinition.EventCode] = [eventCode])"
|
|
240
272
|
```
|
|
273
|
+
|
|
274
|
+
### Last tracking event business days
|
|
275
|
+
|
|
276
|
+
`lastTrackingEventBusinessDays(path, contactId?, contactIdPropertyName?, eventDefinitionName?, orderBy?)` returns an `int?` business-day count for the commodity's last tracking event. Use `contactIdPropertyName: "carrierId"` when each grid row stores its carrier in `Commodity.CustomValues.carrierId`; the resolver reads the row value server-side and filters business calendar availability blocks to org-wide plus that contact.
|
|
@@ -45,6 +45,18 @@ Field names as used in workflow expressions: `{{ entity.name }}`, `{{ entity.cus
|
|
|
45
45
|
| `lastModified` | `DateTime` | |
|
|
46
46
|
| `lastModifiedBy` | `string` | User ID |
|
|
47
47
|
|
|
48
|
+
When an organization user's profile first/last name changes, linked contact profile names can be regenerated from trimmed `contactFirstName` + `contactLastName` so `name` remains in sync with the person display name.
|
|
49
|
+
|
|
50
|
+
### Import behavior
|
|
51
|
+
|
|
52
|
+
Contact CSV/import handling protects division scoping:
|
|
53
|
+
|
|
54
|
+
- `DivisionId` values are accepted only when they belong to the target organization. Foreign, stale, or unparsable division IDs are removed from the import row.
|
|
55
|
+
- For existing contacts, an invalid `DivisionId` is ignored so the current division is not overwritten.
|
|
56
|
+
- For new contacts, if no valid division remains, the contact is created in the importing user's division when available.
|
|
57
|
+
- Nested `division.divisionName` input is resolved to an organization division and converted to `DivisionId`; the nested `division` object is not persisted.
|
|
58
|
+
- Empty strings are stripped from add/update fields so blank CSV cells do not clear existing values or break enum/numeric conversions.
|
|
59
|
+
|
|
48
60
|
## Navigation Properties
|
|
49
61
|
|
|
50
62
|
| Field | Type | Notes |
|
|
@@ -78,6 +90,7 @@ Field names as used in workflow expressions: `{{ entity.name }}`, `{{ entity.cus
|
|
|
78
90
|
| `getContactAddressByType(addressType)` | `[ContactAddress]` | Filter by "Billing", "Shipping", "Other" |
|
|
79
91
|
| `getFirstContactAddressByType(addressType)` | `ContactAddress` | First match |
|
|
80
92
|
| `getContactAttachments(filter, orderBy)` | `[Attachment]` | |
|
|
93
|
+
| `getContactAddresses(filter, orderBy)` | `[ContactAddress]` | Contact-scoped address resolver with server-side Lucene filtering and sorting |
|
|
81
94
|
| `getCustomValuesAttachment(filter)` | `Attachment` | From customValues `attachmentId` key |
|
|
82
95
|
| `availableCredit` | `[AvailableCreditByCurrency]` | Carrier/Customer only |
|
|
83
96
|
| `changeHistory(startDate, endDate, maxResults)` | `[ChangeHistory]` | Audit trail |
|
|
@@ -101,6 +114,7 @@ Field names as used in workflow expressions: `{{ entity.name }}`, `{{ entity.cus
|
|
|
101
114
|
| `Store` | 13 | |
|
|
102
115
|
| `ContactUser` | 14 | User associated with contact |
|
|
103
116
|
| `USPPI` | 15 | US Principal Party in Interest |
|
|
117
|
+
| `Port` | 16 | Port contact/location reference |
|
|
104
118
|
|
|
105
119
|
## ContactAddress Sub-Entity
|
|
106
120
|
|
|
@@ -115,6 +129,7 @@ Field names as used in workflow expressions: `{{ entity.name }}`, `{{ entity.cus
|
|
|
115
129
|
| `stateCode` | `string?` | FK to State |
|
|
116
130
|
| `postalCode` | `string?` | |
|
|
117
131
|
| `isInactive` | `bool?` | |
|
|
132
|
+
| `name` | `string?` | Optional label for the address/location (max 200 chars) |
|
|
118
133
|
| `latitude` | `double?` | From Location.Y (GraphQL resolved) |
|
|
119
134
|
| `longitude` | `double?` | From Location.X (GraphQL resolved) |
|
|
120
135
|
| `customValues` | `Dictionary` | Own customValues (separate from Contact) |
|
|
@@ -123,6 +138,10 @@ Field names as used in workflow expressions: `{{ entity.name }}`, `{{ entity.cus
|
|
|
123
138
|
|
|
124
139
|
GraphQL resolver: `formattedAddress(outputFormat, addressFormat, lang, multiline)` — formatted string.
|
|
125
140
|
|
|
141
|
+
**ContactAddress import notes:** `ContactAddress/Import@1` can update by `ContactAddressId` or `contactAddressId` first, then falls back to business-key matching. Rows are bound as dictionaries, so exported primary-key columns survive re-import even when the casing is camelCase. Export grids should include `contactAddressId`/entity keys when data is intended for re-import. Import aliases `City` to `cityName`, resolves `StateName` to `stateCode`, and accepts `Longitude`/`Latitude` for `location`.
|
|
142
|
+
|
|
143
|
+
Module default-data imports support `entity: "contact"` with `keys` for natural-key matching and `overwrite` for update-vs-skip behavior. Use `ContactType: Port` for port/location contacts when appropriate.
|
|
144
|
+
|
|
126
145
|
## Other Related Enums
|
|
127
146
|
|
|
128
147
|
| Enum | Values |
|
|
@@ -134,6 +153,33 @@ GraphQL resolver: `formattedAddress(outputFormat, addressFormat, lang, multiline
|
|
|
134
153
|
| `ContactStatusStage` | Active=0, Inactive=1 |
|
|
135
154
|
| `PaymentType` | Card=1, AccountCredit=2, Cash=3, Check=4, BankTransfer=5, Other=7 |
|
|
136
155
|
|
|
156
|
+
## Contact Import
|
|
157
|
+
|
|
158
|
+
GraphQL `importContacts` accepts CSV, JSON, or XLSX uploads and imports rows as dynamic contact field dictionaries.
|
|
159
|
+
|
|
160
|
+
Important rules:
|
|
161
|
+
- Existing contacts match by `ContactId`; otherwise a new contact is created.
|
|
162
|
+
- `ContactId` and `OrganizationId` are primary key fields and are filtered out before create/update field application.
|
|
163
|
+
- Empty strings and nulls are skipped on updates; blank spreadsheet cells do not erase existing values.
|
|
164
|
+
- New contacts require `Name`; rows without a non-empty name are skipped.
|
|
165
|
+
- `ContactType` can come from the row, from the mutation-level `contactType` default, or finally defaults to `Contact`.
|
|
166
|
+
- Nested division objects can set the primary division by name: `division.divisionName` resolves to `divisionId` within the same organization.
|
|
167
|
+
|
|
168
|
+
Example mutation input:
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"organizationId": 1,
|
|
173
|
+
"fileUploadUrl": "uploads/imports/contacts.xlsx",
|
|
174
|
+
"contactType": "Customer",
|
|
175
|
+
"columnMappings": {
|
|
176
|
+
"Name": "Company Name",
|
|
177
|
+
"EmailAddress": "Email",
|
|
178
|
+
"Division.DivisionName": "Division"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
137
183
|
## CustomValues
|
|
138
184
|
|
|
139
185
|
`Dictionary<string, object?>` stored as PostgreSQL `jsonb`. Access in workflows:
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Dispatch Routing Entity Field Reference
|
|
2
|
+
|
|
3
|
+
Dispatch routing covers reusable weekly route templates, daily dispatch routes, route statuses, stop statuses, and stops.
|
|
4
|
+
|
|
5
|
+
> **Proposed API:** The Order Move entities and fields in the section below come from the backend
|
|
6
|
+
> design specification and are not deployed yet. Do not generate production queries or mutations
|
|
7
|
+
> against them until implementation is confirmed.
|
|
8
|
+
|
|
9
|
+
## DispatchRouteType Enum
|
|
10
|
+
|
|
11
|
+
| Value | Notes |
|
|
12
|
+
|-------|-------|
|
|
13
|
+
| `Delivery` | Delivery route or stop |
|
|
14
|
+
| `Pickup` | Pickup route or stop |
|
|
15
|
+
| `PickupDelivery` | Combined pickup-and-delivery route or stop |
|
|
16
|
+
|
|
17
|
+
## DispatchRouteStatus
|
|
18
|
+
|
|
19
|
+
| Field | Type | Notes |
|
|
20
|
+
|-------|------|-------|
|
|
21
|
+
| `dispatchRouteStatusId` | `int` | PK |
|
|
22
|
+
| `organizationId` | `int` | Tenant scope |
|
|
23
|
+
| `statusName` | `string` | Display name |
|
|
24
|
+
| `statusDescription` | `string?` | Optional description |
|
|
25
|
+
| `statusStage` | `StatusStage` | `Pending`, `InProgress`, `Completed` |
|
|
26
|
+
| `routeType` | `DispatchRouteType?` | Null applies to any route type |
|
|
27
|
+
| `priority` | `int` | Display sort order |
|
|
28
|
+
| `color` | `string?` | Hex/display color |
|
|
29
|
+
| `customValues` | `Dictionary` | jsonb |
|
|
30
|
+
|
|
31
|
+
## DispatchRouteStopStatus
|
|
32
|
+
|
|
33
|
+
| Field | Type | Notes |
|
|
34
|
+
|-------|------|-------|
|
|
35
|
+
| `dispatchRouteStopStatusId` | `int` | PK |
|
|
36
|
+
| `organizationId` | `int` | Tenant scope |
|
|
37
|
+
| `statusName` | `string` | Display name; unique per organization for active statuses |
|
|
38
|
+
| `statusDescription` | `string?` | Optional description |
|
|
39
|
+
| `statusStage` | `StatusStage` | `Pending`, `InProgress`, `Completed` |
|
|
40
|
+
| `stopType` | `DispatchRouteType?` | Null applies to any stop type |
|
|
41
|
+
| `priority` | `int` | Display sort order |
|
|
42
|
+
| `color` | `string?` | Hex/display color |
|
|
43
|
+
| `customValues` | `Dictionary` | jsonb |
|
|
44
|
+
| `isDeleted` | `bool` | Soft-delete flag |
|
|
45
|
+
|
|
46
|
+
## DispatchRouteTemplate
|
|
47
|
+
|
|
48
|
+
| Field | Type | Notes |
|
|
49
|
+
|-------|------|-------|
|
|
50
|
+
| `dispatchRouteTemplateId` | `int` | PK |
|
|
51
|
+
| `organizationId` | `int` | Tenant scope |
|
|
52
|
+
| `name` | `string` | Template name |
|
|
53
|
+
| `routeType` | `DispatchRouteType` | `Delivery`, `Pickup`, or `PickupDelivery` |
|
|
54
|
+
| `daysOfWeek` | `DayOfWeek[]` | Matching generation days |
|
|
55
|
+
| `divisionId` | `int?` | FK to Division |
|
|
56
|
+
| `equipmentTypeId` | `int?` | Planned equipment type |
|
|
57
|
+
| `enabled` | `bool` | Generation skips disabled templates |
|
|
58
|
+
| `stops` | `[DispatchRouteTemplateStop]` | Ordered by `sequence` |
|
|
59
|
+
| `customValues` | `Dictionary` | jsonb |
|
|
60
|
+
|
|
61
|
+
## DispatchRouteTemplateStop
|
|
62
|
+
|
|
63
|
+
| Field | Type | Notes |
|
|
64
|
+
|-------|------|-------|
|
|
65
|
+
| `dispatchRouteTemplateStopId` | `int` | PK |
|
|
66
|
+
| `dispatchRouteTemplateId` | `int` | Parent template |
|
|
67
|
+
| `stopContactId` | `int?` | Store/location contact (nullable; use with or without `contactAddressId`) |
|
|
68
|
+
| `contactAddressId` | `int?` | Optional stop address |
|
|
69
|
+
| `stopType` | `DispatchRouteType` | Defaults to template route type when omitted |
|
|
70
|
+
| `sequence` | `int` | 1-based order |
|
|
71
|
+
| `estimatedServiceMinutes` | `int?` | Planned service time |
|
|
72
|
+
| `customValues` | `Dictionary` | jsonb (use for ad-hoc address when not using a contact) |
|
|
73
|
+
|
|
74
|
+
## DispatchRoute
|
|
75
|
+
|
|
76
|
+
| Field | Type | Notes |
|
|
77
|
+
|-------|------|-------|
|
|
78
|
+
| `dispatchRouteId` | `int` | PK |
|
|
79
|
+
| `organizationId` | `int` | Tenant scope |
|
|
80
|
+
| `name` | `string` | Route name |
|
|
81
|
+
| `routeType` | `DispatchRouteType` | `Delivery`, `Pickup`, or `PickupDelivery` |
|
|
82
|
+
| `routeDate` | `Date` | `yyyy-MM-dd` |
|
|
83
|
+
| `dispatchRouteStatusId` | `int` | FK to DispatchRouteStatus |
|
|
84
|
+
| `dispatchRouteTemplateId` | `int?` | Source template when generated |
|
|
85
|
+
| `divisionId` | `int?` | FK to Division |
|
|
86
|
+
| `driverContactId` | `int?` | Driver contact assignment |
|
|
87
|
+
| `equipmentId` | `int?` | Equipment assignment |
|
|
88
|
+
| `isDraft` | `bool` | Generated/new routes start as draft |
|
|
89
|
+
| `isInactive` | `bool` | Soft-delete flag |
|
|
90
|
+
| `collapsedIntoDispatchRouteId` | `int?` | Route merge target |
|
|
91
|
+
| `stops` | `[DispatchRouteStop]` | Ordered by `plannedSequence` |
|
|
92
|
+
| `trackingEvents` | `[TrackingEvent]` | Tracking events linked directly to the route |
|
|
93
|
+
| `customValues` | `Dictionary` | jsonb |
|
|
94
|
+
|
|
95
|
+
## DispatchRouteStop
|
|
96
|
+
|
|
97
|
+
| Field | Type | Notes |
|
|
98
|
+
|-------|------|-------|
|
|
99
|
+
| `dispatchRouteStopId` | `int` | PK |
|
|
100
|
+
| `dispatchRouteId` | `int` | Parent route |
|
|
101
|
+
| `stopContactId` | `int?` | Store/location contact (nullable; use with or without `contactAddressId`) |
|
|
102
|
+
| `contactAddressId` | `int?` | Optional stop address |
|
|
103
|
+
| `stopType` | `DispatchRouteType` | Defaults to route route type when omitted |
|
|
104
|
+
| `plannedSequence` | `int` | Planned 1-based order |
|
|
105
|
+
| `actualSequence` | `int?` | Actual execution order |
|
|
106
|
+
| `estimatedServiceMinutes` | `int?` | Planned service time |
|
|
107
|
+
| `dispatchRouteStopStatusId` | `int?` | FK to DispatchRouteStopStatus |
|
|
108
|
+
| `dispatchRouteStopStatus` | `DispatchRouteStopStatus?` | Optional expanded stop status |
|
|
109
|
+
| `actualArrivalTime` | `DateTime?` | Actual arrival timestamp |
|
|
110
|
+
| `actualCompletionTime` | `DateTime?` | Actual completion timestamp |
|
|
111
|
+
| `orderMoveId` | `int?` | Optional move this trip stop executes |
|
|
112
|
+
| `orderMove` | `OrderMove?` | Expandable move navigation |
|
|
113
|
+
| `orderIds` | `int[]` | Attached order IDs; always projected |
|
|
114
|
+
| `orders` | `[DispatchRouteStopOrder]` | Attached order links; expand `order` when needed |
|
|
115
|
+
| `trackingEvents` | `[TrackingEvent]` | Tracking events linked directly to this stop |
|
|
116
|
+
| `customValues` | `Dictionary` | jsonb (use for ad-hoc address when not using a contact) |
|
|
117
|
+
|
|
118
|
+
## DispatchRouteStopOrder
|
|
119
|
+
|
|
120
|
+
| Field | Type | Notes |
|
|
121
|
+
|-------|------|-------|
|
|
122
|
+
| `dispatchRouteStopOrderId` | `int` | PK |
|
|
123
|
+
| `dispatchRouteStopId` | `int` | Parent stop |
|
|
124
|
+
| `orderId` | `int` | Attached order |
|
|
125
|
+
| `order` | `Order?` | Optional expanded order |
|
|
126
|
+
| `dispatchRouteStop` | `DispatchRouteStop?` | Optional expanded stop when reached from `order.dispatchRouteStopOrders` |
|
|
127
|
+
| `customValues` | `Dictionary` | jsonb on the stop/order link |
|
|
128
|
+
|
|
129
|
+
## Order Moves
|
|
130
|
+
|
|
131
|
+
An order owns ordered `OrderMove` records, and each move owns ordered `OrderMoveLeg` records.
|
|
132
|
+
Vertical-specific routing details belong in `customValues`.
|
|
133
|
+
|
|
134
|
+
### OrderMove
|
|
135
|
+
|
|
136
|
+
| Field | Type | Notes |
|
|
137
|
+
|-------|------|-------|
|
|
138
|
+
| `orderMoveId` | `int` | PK |
|
|
139
|
+
| `organizationId` | `int` | Tenant scope |
|
|
140
|
+
| `orderId` | `int` | Parent order |
|
|
141
|
+
| `sequence` | `int` | 1-based order within the order |
|
|
142
|
+
| `name` | `string?` | Optional label |
|
|
143
|
+
| `assignedDriverContactId` | `int?` | FK to Contact |
|
|
144
|
+
| `orderMoveStatusId` | `int?` | FK to OrderMoveStatus |
|
|
145
|
+
| `startDate` | `DateTime?` | UTC start |
|
|
146
|
+
| `endDate` | `DateTime?` | UTC end |
|
|
147
|
+
| `customValues` | `Dictionary` | Vertical-specific jsonb data |
|
|
148
|
+
| `orderMoveLegs` | `[OrderMoveLeg]` | Ordered by `sequence` |
|
|
149
|
+
| `dispatchRouteStops` | `[DispatchRouteStop]` | Stops executing this move, ordered by route then planned sequence; may span routes |
|
|
150
|
+
| `trackingEvents` | `[TrackingEvent]` | Move-level milestones |
|
|
151
|
+
|
|
152
|
+
### OrderMoveLeg
|
|
153
|
+
|
|
154
|
+
| Field | Type | Notes |
|
|
155
|
+
|-------|------|-------|
|
|
156
|
+
| `orderMoveLegId` | `int` | PK |
|
|
157
|
+
| `orderMoveId` | `int` | Parent move |
|
|
158
|
+
| `sequence` | `int` | 1-based order within the move |
|
|
159
|
+
| `name` | `string?` | Optional label |
|
|
160
|
+
| `orderMoveLegStatusId` | `int?` | FK to OrderMoveLegStatus |
|
|
161
|
+
| `startDate` | `DateTime?` | UTC start |
|
|
162
|
+
| `endDate` | `DateTime?` | UTC end |
|
|
163
|
+
| `customValues` | `Dictionary` | Location, event, appointment, and metric data |
|
|
164
|
+
| `trackingEvents` | `[TrackingEvent]` | Leg-level milestones |
|
|
165
|
+
|
|
166
|
+
`OrderMoveStatus` and `OrderMoveLegStatus` are organization-scoped status dictionaries with
|
|
167
|
+
`statusName`, `statusDescription`, `statusStage`, `priority`, `color`, and `customValues`.
|
|
168
|
+
Moves support `OrderMove/Create@1`, `OrderMove/Update@1`, and `OrderMove/Delete@1` workflow tasks.
|
|
169
|
+
|
|
170
|
+
Order creation may include `orderMoves`; the new order supplies their `orderId`, moves default
|
|
171
|
+
to array-position sequence, and nested legs receive 1-based array-order sequences.
|
|
172
|
+
`Order/Import@1` also accepts moves and nested legs. Existing records match by ID, then by
|
|
173
|
+
`ImportOrderOptions.OrderMoveMatchByFields` / `OrderMoveLegMatchByFields` (dotted
|
|
174
|
+
`customValues` paths are supported), and finally by sequence or array position. New records
|
|
175
|
+
are appended and omitted existing records are not deleted.
|
|
176
|
+
|
|
177
|
+
## GraphQL Notes
|
|
178
|
+
|
|
179
|
+
- Queries: `dispatchRouteStatus`, `dispatchRouteStatuses`, `dispatchRouteStopStatus`, `dispatchRouteStopStatuses`, `dispatchRouteTemplate`, `dispatchRouteTemplates`, `dispatchRoute`, `dispatchRoutes`.
|
|
180
|
+
- Order move queries: `orderMove`, `orderMoves`, `orderMoveStatus`, `orderMoveStatuses`, `orderMoveLegStatus`, and `orderMoveLegStatuses`.
|
|
181
|
+
- Nested order resolvers: `getOrderMoves(filter, orderBy)` returns pending or statusless moves (defaulted to sequence order), while `getOrderMove(filter, orderBy)` returns the first matching pending or statusless move. Moves whose status stage is no longer `Pending` are excluded.
|
|
182
|
+
- Order move mutations create, sparse-update, and soft-delete moves; create/update accepts nested `orderMoveLegs`, reconciled by ID and array order. Omitted or `isDeleted: true` legs are soft-deleted, new already-deleted legs are ignored, and only active legs consume sequence positions.
|
|
183
|
+
- A dynamic order update can supply `orderMoves` to reconcile the full collection with the same rules. New moves inherit `organizationId` and `orderId` from the aggregate; omitted or explicitly deleted moves are soft-deleted; active moves and nested legs are resequenced from 1.
|
|
184
|
+
- Mutations use `input: { organizationId, values }` and return payload fields named `dispatchRouteStatus`, `dispatchRouteStopStatus`, `dispatchRouteTemplate`, `dispatchRoute`, or `generateDispatchRoutesResult`.
|
|
185
|
+
- Stop status mutations: `createDispatchRouteStopStatus`, `updateDispatchRouteStopStatus`, `deleteDispatchRouteStopStatus`.
|
|
186
|
+
- Route stops can be anchored by location (`stopContactId`, `contactAddressId`, ad-hoc `customValues`) or by attached `orderIds`.
|
|
187
|
+
- Route stops optionally expand `orderMove`; the inverse `orderMove.dispatchRouteStops` collection supports filtering and may span multiple dispatch routes.
|
|
188
|
+
- Create accepts nested route stops with `dispatchRouteStopStatusId`, `orderMoveId`, and `orderIds`. `orderMoveId` links the new stop to the move it executes. Dynamic route updates and stop add/update mutations also accept `dispatchRouteStopStatusId` and `orderIds`; statuses are organization-scoped and validated against stop type, and when `orderIds` is present, the attached orders are reconciled to exactly those IDs after organization validation.
|
|
189
|
+
- `updateDispatchRouteStop` uses a dynamic `values` map. It sparse-updates only supplied keys, treats an explicit `null` as clearing a nullable field, and rejects unknown keys. Use `actualArrivalTime` and `actualCompletionTime` to stamp or clear actual stop times without echoing unrelated fields.
|
|
190
|
+
- Dynamic route/template updates can replace the full `stops` array: existing stops with IDs are sparse-updated, new stops are inserted, omitted existing stops are soft-deleted, and sequence/plannedSequence is reassigned from array order. Dedicated stop add/update/remove/reorder mutations remain available for targeted edits.
|
|
191
|
+
- Orders expose `relatedDispatchRoutes(filter, orderBy)` for routes linked through stop order attachments; draft orders return no related routes.
|
|
192
|
+
- Orders expose raw stop membership links through `dispatchRouteStopOrders`; use nested paths such as `dispatchRouteStopOrders.dispatchRouteStop.dispatchRouteStopStatus.statusStage` when filtering orders by route-stop assignment or completion state.
|
|
193
|
+
- Tracking events can be attached directly to dispatch routes and dispatch route stops. Use route-level events for whole-route milestones and stop-level events for arrival, completion, exception, or proof-of-service milestones tied to one stop.
|
|
194
|
+
- Workflow tasks support these links: `TrackingEvent/Create@1` accepts `dispatchRouteId` or `dispatchRouteStopId`; `TrackingEvent/Import@1` requires exactly one target among `orderId`, `dispatchRouteId`, and `dispatchRouteStopId`.
|
|
195
|
+
- Route generation is idempotent per template/date and creates draft routes.
|
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
- City
|
|
7
7
|
- PostalCode
|
|
8
8
|
- Port
|
|
9
|
+
- Terminal
|
|
9
10
|
- Vessel
|
|
10
11
|
- CustomCode
|
|
11
12
|
- ModeOfTransportation
|
|
12
13
|
|
|
13
|
-
Country, State, City, PostalCode, Port, Vessel, CustomCode, ModeOfTransportation.
|
|
14
|
+
Country, State, City, PostalCode, Port, Terminal, Vessel, CustomCode, ModeOfTransportation.
|
|
14
15
|
|
|
15
16
|
## Country
|
|
16
17
|
|
|
@@ -110,6 +111,30 @@ String-based PK (e.g., UN/LOCODE).
|
|
|
110
111
|
|
|
111
112
|
---
|
|
112
113
|
|
|
114
|
+
## Terminal
|
|
115
|
+
|
|
116
|
+
Organization-scoped terminal / operating location (facility record). Optional Port linkage. Soft deleted; codes are unique only among active terminals in the same organization.
|
|
117
|
+
|
|
118
|
+
| Field | Type | Notes |
|
|
119
|
+
|-------|------|-------|
|
|
120
|
+
| `terminalId` | `int` | PK |
|
|
121
|
+
| `organizationId` | `int` | Required; tenant scope |
|
|
122
|
+
| `name` | `string` | Required display name |
|
|
123
|
+
| `code` | `string?` | Optional; unique per organization when not null and `isDeleted=false` |
|
|
124
|
+
| `portId` | `string?` | Optional FK to Port (`organizationId` + `portId`) |
|
|
125
|
+
| `customValues` | `Dictionary` | jsonb; searchable |
|
|
126
|
+
| `isDeleted` | `bool` | Soft delete flag; default false; default queries filter deleted rows |
|
|
127
|
+
| `created` / `createdBy` | `DateTime` / `string` | Audit fields |
|
|
128
|
+
| `lastModified` / `lastModifiedBy` | `DateTime` / `string` | Audit fields |
|
|
129
|
+
|
|
130
|
+
**Navigation:** `organization`, `port`, `createdUser`, `updatedUser`
|
|
131
|
+
|
|
132
|
+
**GraphQL:** `terminal(organizationId, terminalId)`, `terminals(organizationId, filter, search, orderBy)`, `createTerminal`, `updateTerminal`, `deleteTerminal`.
|
|
133
|
+
|
|
134
|
+
**Search:** `name`, `code`, and `customValues`.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
113
138
|
## Vessel
|
|
114
139
|
|
|
115
140
|
| Field | Type | Notes |
|
|
@@ -36,9 +36,13 @@ Job groups related orders and accounting transactions.
|
|
|
36
36
|
| `accountingTransactions` | `[AccountingTransaction]` | Via JobAccountingTransaction join |
|
|
37
37
|
| `commodities` | `[Commodity]` | Direct FK |
|
|
38
38
|
|
|
39
|
-
### GraphQL
|
|
39
|
+
### GraphQL Child Resolvers
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
| Field | Type | Notes |
|
|
42
|
+
|-------|------|-------|
|
|
43
|
+
| `getJobOrders(filter)` | `[Order]` | Orders via JobOrder join |
|
|
44
|
+
| `getJobOrder(filter)` | `Order?` | Single order via JobOrder join (UseFirstOrDefault + projection) |
|
|
45
|
+
| `getJobCommodity(filter)` | `Commodity?` | Single commodity linked to job (by JobId) |
|
|
42
46
|
|
|
43
47
|
---
|
|
44
48
|
|
|
@@ -81,7 +81,7 @@ Milestone/tracking event on an order or commodity.
|
|
|
81
81
|
|
|
82
82
|
| Field | Returns | Notes |
|
|
83
83
|
|-------|---------|-------|
|
|
84
|
-
| `businessDays(path: String
|
|
84
|
+
| `businessDays(path: String!, contactId?: Int)` | `int?` | Business days from the date at `path` to today, using the org business calendar. Optional `contactId` scopes availability blocks to org-wide plus matching contact blocks. Instant values are converted to the org timezone before taking the date; date-only values are not timezone-shifted. Returns `null` if path does not resolve or value is not parseable. |
|
|
85
85
|
|
|
86
86
|
---
|
|
87
87
|
|
|
@@ -40,6 +40,7 @@ Field names as used in workflow expressions: `{{ entity.orderId }}`, `{{ entity.
|
|
|
40
40
|
| Field | Type | Notes |
|
|
41
41
|
|-------|------|-------|
|
|
42
42
|
| `orderStatus` | `OrderStatus` | `.statusName`, `.statusStage` |
|
|
43
|
+
| `orderSummary` | `OrderSummaryView` | One-to-one summary view; supports nested sort paths like `orderSummary.totalPieces` |
|
|
43
44
|
| `division` | `Division` | `.name` |
|
|
44
45
|
| `equipmentType` | `EquipmentType` | |
|
|
45
46
|
| `billToContact` | `Contact` | Full contact object |
|
|
@@ -50,6 +51,14 @@ Field names as used in workflow expressions: `{{ entity.orderId }}`, `{{ entity.
|
|
|
50
51
|
| `createdUser` | `User` | `.firstName`, `.lastName`, `.email` |
|
|
51
52
|
| `updatedUser` | `User` | |
|
|
52
53
|
|
|
54
|
+
## Workflow Trigger Payload Notes
|
|
55
|
+
|
|
56
|
+
Order entity triggers include scalar status fields (`orderStatusId`, `orderStatusName`) in the lightweight workflow payload. `orderStatusName` is loaded before mapping when needed, so modified-order workflows and Flow auto-transition expressions can rely on it even if the EF event did not preload the `orderStatus` navigation.
|
|
57
|
+
|
|
58
|
+
## Search Behavior
|
|
59
|
+
|
|
60
|
+
Order GraphQL quick search (`orders(search:)` and `orderGroupBy(search:)`) matches core order fields plus related commodity and inventory item fields. InventoryItem data linked from an order commodity, including child/container commodities, is searchable by `sku`, `productName`, `description`, `modelNumber`, and JSON `customValues`. Use this for warehouse-backed order lookup by SKU, item name, model number, color, size, or similar item attributes.
|
|
61
|
+
|
|
53
62
|
## Collection Properties
|
|
54
63
|
|
|
55
64
|
| Field | Type | Notes |
|
|
@@ -63,8 +72,10 @@ Field names as used in workflow expressions: `{{ entity.orderId }}`, `{{ entity.
|
|
|
63
72
|
| `jobOrders` | `[JobOrder]` | |
|
|
64
73
|
| `orderTags` | `[OrderTag]` | |
|
|
65
74
|
| `orderCarriers` | `[OrderCarrier]` | |
|
|
75
|
+
| `dispatchRouteStopOrders` | `[DispatchRouteStopOrder]` | Stop membership links for this order; expand `dispatchRouteStop` to inspect route, stop status, or stop tracking events |
|
|
66
76
|
| `allTags` | `[OrderAllTagsView]` | View: all tags including from commodities |
|
|
67
77
|
| `allRelatedOrders` | `[OrderRelatedOrdersView]` | Orders sharing commodities |
|
|
78
|
+
| `relatedDispatchRoutes` | `[DispatchRoute]` | Routes linked through dispatch route stop order attachments; supports `filter` and `orderBy`; draft orders return an empty list |
|
|
68
79
|
| `attachmentsSummary` | `OrderAttachmentSummaryView?` | DB view: `.totalCount`, `.hasAny` (active attachments) |
|
|
69
80
|
| `notesSummary` | `OrderNoteSummaryView?` | DB view: `.totalCount`, `.hasAny` (non-deleted notes) |
|
|
70
81
|
| `outgoingLinks` | `[LinkedOrder]` | |
|
|
@@ -98,18 +109,21 @@ These are virtual fields that filter `orderEntities` by type:
|
|
|
98
109
|
| `commoditySummary` | `CommoditySummary` | `.totalWeight`, `.totalPieces`, `.totalQuantity`, `.totalVolume` |
|
|
99
110
|
| `accountingSummary` | `AccountingSummary` | |
|
|
100
111
|
| `getContact(idPropertyName)` | `Contact` | Resolve contact from `customValues[idPropertyName]` |
|
|
112
|
+
| `getTerminal(idPropertyName)` | `Terminal` | Resolve terminal from int `customValues[idPropertyName]` |
|
|
101
113
|
| `getPort(idPropertyName)` | `Port` | Resolve port from `customValues[idPropertyName]` |
|
|
114
|
+
| `getTerminal(idPropertyName)` | `Terminal` | Resolve terminal from `customValues[idPropertyName]` |
|
|
102
115
|
| `getVessel(idPropertyName)` | `Vessel` | Resolve vessel from `customValues[idPropertyName]` |
|
|
103
116
|
| `getCountry(idPropertyName)` | `Country` | Resolve country from `customValues[idPropertyName]` |
|
|
104
117
|
| `getRoute(idPropertyName)` | `Route` | Resolve route from `customValues[idPropertyName]` |
|
|
105
118
|
| `getModeOfTransportation(idPropertyName)` | `ModeOfTransportation` | |
|
|
106
119
|
| `getCustomCode(idPropertyName)` | `CustomCode` | |
|
|
107
120
|
| `getRelatedOrderByProperty(idPropertyName)` | `Order` | Resolve related order from `customValues[idPropertyName]` |
|
|
121
|
+
| `relatedDispatchRoutes(filter, orderBy)` | `[DispatchRoute]` | Routes linked through dispatch route stop order attachments; draft orders return an empty list |
|
|
108
122
|
| `getCharge(chargeDescription)` | `Charge` | Single charge by description |
|
|
109
123
|
| `getChargesByChargeType(chargeType)` | `[Charge]` | Charges filtered by type |
|
|
110
124
|
| `getOrderSummary(weightUnit, volumeUnit, dimensionsUnit)` | `OrderSummary` | |
|
|
111
|
-
| `lastTrackingEvent(eventDefinitionName, orderBy?)` | `TrackingEvent` | Most recent (or earliest) tracking event, resolved via batched DataLoader. `orderBy` is **honoured**: omit or prefix with `-` for DESC (latest event: `COALESCE(EventDate, Created) DESC, TrackingEventId DESC`); no prefix for ASC (earliest event: same columns ASC). Default is DESC. |
|
|
112
|
-
| `businessDays(path: String
|
|
125
|
+
| `lastTrackingEvent(eventDefinitionName, orderBy?)` | `TrackingEvent` | Most recent (or earliest) tracking event, resolved via batched DataLoader. `eventDefinitionName` may contain pipe-separated names such as `Departed|Delivered`; the resolver selects the winner across all matching definitions. `orderBy` is **honoured**: omit or prefix with `-` for DESC (latest event: `COALESCE(EventDate, Created) DESC, TrackingEventId DESC`); no prefix for ASC (earliest event: same columns ASC). Default is DESC. |
|
|
126
|
+
| `businessDays(path: String!, contactId?: Int)` | `int?` | Business days from the date at `path` to today, using the org business calendar. Optional `contactId` scopes availability blocks to org-wide plus matching contact blocks. Instant values are converted to the org timezone before taking the date; date-only values are not timezone-shifted. Returns `null` if path does not resolve or value is not parseable. |
|
|
113
127
|
| `attachmentsSummary` | `OrderAttachmentSummaryGqlDto` | `.totalCount` (int), `.hasAny` (bool) — batched DataLoader, backed by DB view |
|
|
114
128
|
| `notesSummary` | `OrderNoteSummaryGqlDto` | `.totalCount` (int), `.hasAny` (bool) — batched DataLoader, backed by DB view |
|
|
115
129
|
| `notesCount(threadFilter)` | `int` | |
|
|
@@ -179,6 +193,25 @@ inputs:
|
|
|
179
193
|
|
|
180
194
|
**Known system customValues keys:**
|
|
181
195
|
- `modeOfTransportationId` / `modeOfTransportationIdDescription` — transport mode
|
|
196
|
+
- `terminalId` — terminal reference; sortable/filterable with `customValues.terminalId->terminal.name`
|
|
197
|
+
- `deliveryLocationId` — contact-address reference; sortable/filterable with `customValues.deliveryLocationId->contactAddress.name`
|
|
198
|
+
- `returnLocationId` — terminal or contact-address return-location reference; sortable/filterable with `customValues.returnLocationId->terminal.name` or `customValues.returnLocationId->contactAddress.name`
|
|
199
|
+
|
|
200
|
+
**Join expression pattern** — Order queries can sort and filter by properties of entities referenced from `customValues` using `customValues.key->entity.property`. Supported aliases include `contact`, `order`, `modeOfTransportation`, `country`, `terminal`, `contactAddress`, `port`, and `vessel`. The `vessel` alias joins an integer custom value to `Vessel.VesselId`.
|
|
201
|
+
|
|
202
|
+
```graphql
|
|
203
|
+
orders(
|
|
204
|
+
organizationId: 1
|
|
205
|
+
orderBy: "customValues.deliveryLocationId->contactAddress.name"
|
|
206
|
+
filter: "customValues.returnLocationId->contactAddress.name:Chicago*"
|
|
207
|
+
) { items { orderId orderNumber } }
|
|
208
|
+
|
|
209
|
+
orders(
|
|
210
|
+
organizationId: 1
|
|
211
|
+
orderBy: "customValues.portId->port.name"
|
|
212
|
+
orderBy: "customValues.vesselId->vessel.name"
|
|
213
|
+
) { items { orderId orderNumber } }
|
|
214
|
+
```
|
|
182
215
|
|
|
183
216
|
**Resolver pattern** — Many GraphQL fields resolve entities from customValues IDs:
|
|
184
|
-
`getContact(idPropertyName)` reads `customValues[idPropertyName]` as a contact ID and returns the full Contact object. Same pattern for ports, vessels, countries, routes, etc.
|
|
217
|
+
`getContact(idPropertyName)` reads `customValues[idPropertyName]` as a contact ID and returns the full Contact object. `getTerminal(idPropertyName)` uses the same pattern for terminal IDs. Same pattern for ports, vessels, countries, routes, etc.
|