@cxtms/cx-schema 1.0.0 → 1.1.1

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.
Files changed (66) hide show
  1. package/.claude/skills/cx-core/SKILL.md +93 -0
  2. package/.claude/skills/cx-core/ref-entity-accounting.md +173 -0
  3. package/.claude/skills/cx-core/ref-entity-commodity.md +205 -0
  4. package/.claude/skills/cx-core/ref-entity-contact.md +153 -0
  5. package/.claude/skills/cx-core/ref-entity-geography.md +119 -0
  6. package/.claude/skills/cx-core/ref-entity-job.md +77 -0
  7. package/.claude/skills/cx-core/ref-entity-order-sub.md +140 -0
  8. package/.claude/skills/cx-core/ref-entity-order.md +168 -0
  9. package/.claude/skills/cx-core/ref-entity-rate.md +174 -0
  10. package/.claude/skills/cx-core/ref-entity-shared.md +147 -0
  11. package/.claude/skills/cx-core/ref-entity-warehouse.md +110 -0
  12. package/.claude/skills/cx-module/SKILL.md +402 -0
  13. package/.claude/skills/cx-module/ref-components-data.md +286 -0
  14. package/.claude/skills/cx-module/ref-components-display.md +394 -0
  15. package/.claude/skills/cx-module/ref-components-forms.md +362 -0
  16. package/.claude/skills/cx-module/ref-components-interactive.md +306 -0
  17. package/.claude/skills/cx-module/ref-components-layout.md +295 -0
  18. package/.claude/skills/cx-module/ref-components-specialized.md +427 -0
  19. package/.claude/skills/cx-workflow/SKILL.md +330 -0
  20. package/.claude/skills/cx-workflow/ref-accounting.md +66 -0
  21. package/.claude/skills/cx-workflow/ref-communication.md +161 -0
  22. package/.claude/skills/cx-workflow/ref-entity.md +162 -0
  23. package/.claude/skills/cx-workflow/ref-expressions.md +239 -0
  24. package/.claude/skills/cx-workflow/ref-filetransfer.md +80 -0
  25. package/.claude/skills/cx-workflow/ref-flow.md +180 -0
  26. package/.claude/skills/cx-workflow/ref-other.md +120 -0
  27. package/.claude/skills/cx-workflow/ref-query.md +85 -0
  28. package/.claude/skills/cx-workflow/ref-utilities.md +171 -0
  29. package/README.md +34 -34
  30. package/dist/cli.js +545 -35
  31. package/dist/cli.js.map +1 -1
  32. package/dist/types.d.ts +17 -1
  33. package/dist/types.d.ts.map +1 -1
  34. package/dist/workflowValidator.d.ts +31 -0
  35. package/dist/workflowValidator.d.ts.map +1 -1
  36. package/dist/workflowValidator.js +299 -9
  37. package/dist/workflowValidator.js.map +1 -1
  38. package/package.json +4 -3
  39. package/schemas/schema.graphql +2077 -321
  40. package/schemas/workflows/flow/aggregation.json +44 -0
  41. package/schemas/workflows/flow/entity.json +110 -0
  42. package/schemas/workflows/flow/state.json +105 -0
  43. package/schemas/workflows/flow/transition.json +143 -0
  44. package/schemas/workflows/input.json +53 -7
  45. package/schemas/workflows/output.json +20 -0
  46. package/schemas/workflows/trigger.json +4 -0
  47. package/schemas/workflows/variable.json +2 -6
  48. package/schemas/workflows/workflow.json +179 -21
  49. package/scripts/postinstall.js +30 -1
  50. package/templates/module-configuration.yaml +110 -0
  51. package/templates/module-form.yaml +152 -0
  52. package/templates/module-grid.yaml +229 -0
  53. package/templates/module-select.yaml +139 -0
  54. package/templates/module.yaml +3 -3
  55. package/templates/workflow-api-tracking.yaml +189 -0
  56. package/templates/workflow-basic.yaml +76 -0
  57. package/templates/workflow-document.yaml +155 -0
  58. package/templates/workflow-entity-trigger.yaml +90 -0
  59. package/templates/workflow-ftp-edi.yaml +158 -0
  60. package/templates/workflow-ftp-tracking.yaml +161 -0
  61. package/templates/workflow-mcp-tool.yaml +112 -0
  62. package/templates/workflow-public-api.yaml +135 -0
  63. package/templates/workflow-scheduled.yaml +125 -0
  64. package/templates/workflow-utility.yaml +96 -0
  65. package/templates/workflow-webhook.yaml +128 -0
  66. package/templates/workflow.yaml +52 -12
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: cx-core
3
+ description: Shared CargoXplorer entity field reference — domain entities, field names, enums, and customValues patterns
4
+ argument-hint: <entity name or question about fields>
5
+ ---
6
+
7
+ Shared domain reference for CargoXplorer entities. Used by `cx-workflow` and `cx-module` skills for entity field names, types, navigation properties, enums, and customValues extension patterns.
8
+
9
+ ## Entity Field Reference
10
+
11
+ ### Primary Entities
12
+
13
+ !cat .claude/skills/cx-core/ref-entity-order.md
14
+ !cat .claude/skills/cx-core/ref-entity-contact.md
15
+ !cat .claude/skills/cx-core/ref-entity-commodity.md
16
+ !cat .claude/skills/cx-core/ref-entity-accounting.md
17
+
18
+ ### Order Sub-Entities & Related
19
+
20
+ !cat .claude/skills/cx-core/ref-entity-order-sub.md
21
+ !cat .claude/skills/cx-core/ref-entity-job.md
22
+
23
+ ### Pricing & Accounting Lookups
24
+
25
+ !cat .claude/skills/cx-core/ref-entity-rate.md
26
+
27
+ ### Shared & Lookup Entities
28
+
29
+ !cat .claude/skills/cx-core/ref-entity-shared.md
30
+ !cat .claude/skills/cx-core/ref-entity-geography.md
31
+
32
+ ### Warehouse & Inventory
33
+
34
+ !cat .claude/skills/cx-core/ref-entity-warehouse.md
35
+
36
+ | Category | Entities | Reference |
37
+ |----------|----------|-----------|
38
+ | **Primary** | Order, Contact, Commodity, AccountingTransaction | ref-entity-order/contact/commodity/accounting.md |
39
+ | **Order sub** | OrderEntity, TrackingEvent, EventDefinition, LinkedOrder, OrderDocument | ref-entity-order-sub.md |
40
+ | **Job** | Job, JobOrder, JobStatus | ref-entity-job.md |
41
+ | **Pricing** | Rate, Lane, Discount, AccountingItem, AccountingAccount, PaymentTerm | ref-entity-rate.md |
42
+ | **Shared** | Tag, Attachment, Division, EquipmentType, PackageType, Note/NoteThread | ref-entity-shared.md |
43
+ | **Geography** | Country, State, City, Port, Vessel, CustomCode, ModeOfTransportation | ref-entity-geography.md |
44
+ | **Warehouse** | InventoryItem, WarehouseLocation, CargoMovement (Order variant) | ref-entity-warehouse.md |
45
+
46
+ ## CustomValues Pattern
47
+
48
+ Most entities have `customValues` — a `Dictionary<string, object?>` stored as PostgreSQL `jsonb`:
49
+
50
+ - **Access (workflow)**: `{{ entity.customValues.fieldName }}` or `{{ entity.customValues['field-name'] }}`
51
+ - **Access (module)**: `customValues.fieldName` in GraphQL sort/filter paths
52
+ - **Update (workflow)**: `CustomValues.fieldName: "value"` (dot notation) or `customValues: { field: "value" }` (bulk merge)
53
+ - **Merge semantics**: upserts keys — does **not** replace entire dictionary
54
+ - **Full-text searchable**: included in PostgreSQL `tsvector` via `jsonb_to_tsvector`
55
+ - **GraphQL sort/filter**: use `CustomValues.fieldName` path — translates to `jsonb_extract_path_text`
56
+
57
+ ### Entities with customValues
58
+
59
+ **Primary entities**: Order, Contact, Commodity, AccountingTransaction, Charge, Payment
60
+
61
+ **Sub-entities**: OrderEntity, OrderCommodity, ContactAddress, ContactPaymentMethod, CommodityType, CommodityTag, OrderTag, LinkedOrder, InventoryItemTag
62
+
63
+ **Lookup entities**: Job, JobStatus, Tag, Attachment, EventDefinition, Rate, Lane, Discount, AccountingItem, Port, Country, State, City, ModeOfTransportation
64
+
65
+ **Without customValues**: Division, EquipmentType, PackageType, Vessel, CustomCode, AccountingAccount, PaymentTerm, WarehouseLocation, JobOrder
66
+
67
+ ### Audit Fields (AuditableEntity)
68
+
69
+ Most entities inherit these fields:
70
+
71
+ | Field | Type |
72
+ |-------|------|
73
+ | `created` | `DateTime` |
74
+ | `createdBy` | `string` (user ID) |
75
+ | `lastModified` | `DateTime` |
76
+ | `lastModifiedBy` | `string` (user ID) |
77
+ | `createdUser` | `User` navigation |
78
+ | `updatedUser` | `User` navigation |
79
+
80
+ ### Entity Kind Mapping
81
+
82
+ Used in module `entityKind` and Flow workflow `entity.name`:
83
+
84
+ | EntityKind | Entities |
85
+ |------------|----------|
86
+ | `Order` | Order (all types: Brokerage, ParcelShipment, Quote, WarehouseReceipt, etc.) |
87
+ | `Contact` | Contact (all types: Customer, Carrier, Vendor, Driver, Employee, etc.) |
88
+ | `OrderEntity` | OrderEntity (Shipper, Consignee, Carrier roles on an order) |
89
+ | `AccountingTransaction` | Invoice, Bill, CreditMemo |
90
+ | `Commodity` | Commodity |
91
+ | `Calendar` | CalendarEntity |
92
+ | `CalendarEvent` | CalendarEvent |
93
+ | `Other` | Any custom entity |
@@ -0,0 +1,173 @@
1
+ # Accounting Transaction Entity Field Reference
2
+
3
+ Field names as used in workflow expressions: `{{ entity.transactionNumber }}`, `{{ entity.customValues.myField }}`.
4
+
5
+ ## AccountingTransaction Fields
6
+
7
+ ### Scalar Fields
8
+
9
+ | Field | Type | Notes |
10
+ |-------|------|-------|
11
+ | `accountingTransactionId` | `int` | Primary key |
12
+ | `organizationId` | `int` | Tenant scope |
13
+ | `divisionId` | `int` | Division scope |
14
+ | `transactionNumber` | `string` | Business-facing number |
15
+ | `transactionDate` | `DateTime` | Date of transaction |
16
+ | `dueDate` | `DateTime` | Payment due date |
17
+ | `paidDate` | `DateTime?` | When fully paid |
18
+ | `accountingTransactionType` | `AccountingTransactionType` enum | Invoice, Bill, CreditMemo |
19
+ | `accountingTransactionStatus` | `AccountingTransactionStatus` enum | Open, Paid, Void |
20
+ | `amountDue` | `decimal` | Charges total minus payments |
21
+ | `amountPaid` | `decimal` | Sum of applied payments |
22
+ | `paidAs` | `PaidAs` enum | Prepaid, Collect |
23
+ | `isDraft` | `bool` | Not yet submitted |
24
+ | `note` | `string?` | |
25
+ | `accountId` | `int?` | FK to AccountingAccount |
26
+ | `applyToContactID` | `int?` | FK to Contact (customer/vendor) |
27
+ | `billToContactAddressId` | `int?` | FK to ContactAddress |
28
+ | `paymentTermsId` | `int?` | FK to PaymentTerm |
29
+ | `created` | `DateTime` | |
30
+ | `createdBy` | `string` | User ID |
31
+ | `lastModified` | `DateTime` | |
32
+ | `lastModifiedBy` | `string` | User ID |
33
+
34
+ ### Navigation Properties
35
+
36
+ | Field | Type | Notes |
37
+ |-------|------|-------|
38
+ | `applyToContact` | `Contact` | Customer or vendor |
39
+ | `billToContactAddress` | `ContactAddress` | Billing address |
40
+ | `division` | `Division` | |
41
+ | `organization` | `Organization` | |
42
+ | `paymentTerm` | `PaymentTerm` | |
43
+ | `account` | `AccountingAccount` | |
44
+ | `createdUser` | `User` | |
45
+ | `updatedUser` | `User` | |
46
+
47
+ ### Collection Properties
48
+
49
+ | Field | Type | Notes |
50
+ |-------|------|-------|
51
+ | `charges` | `[Charge]` | Via AccountingTransactionCharges join |
52
+ | `payments` | `[Payment]` | Via AccountingTransactionPayment join |
53
+ | `jobs` | `[Job]` | Via JobAccountingTransaction join |
54
+
55
+ ### Computed/Resolved GraphQL Fields
56
+
57
+ | Field | Returns | Notes |
58
+ |-------|---------|-------|
59
+ | `getChargesTotalAmount` | `decimal` | Sum of non-void charges |
60
+ | `getAmountDue` | `decimal` | Charges total minus amountPaid |
61
+ | `getRelatedOrders(filter, orderBy)` | `[Order]` | Orders linked via charges |
62
+ | `getRelatedOrdersByOrderType(orderType)` | `[Order]` | Filtered by order type |
63
+ | `getRelatedOrderByOrderType(orderType)` | `Order` | First match |
64
+ | `changeHistory(startDate, endDate, maxResults)` | `[ChangeHistory]` | Audit trail |
65
+
66
+ ---
67
+
68
+ ## Charge Entity
69
+
70
+ | Field | Type | Notes |
71
+ |-------|------|-------|
72
+ | `chargeId` | `int` | Primary key |
73
+ | `organizationId` | `int?` | |
74
+ | `description` | `string?` | |
75
+ | `chargeType` | `ChargeType` enum | Income, Expense, Credit |
76
+ | `chargeStatus` | `ChargeStatus` enum | Pending, Open, Posted, Paid, Void |
77
+ | `amount` | `decimal` | Calculated: `quantity * price` |
78
+ | `price` | `decimal` | Unit price |
79
+ | `quantity` | `decimal` | |
80
+ | `unit` | `string?` | e.g., "TV", "Pallet", "Kg" |
81
+ | `applyBy` | `ApplyBy` enum | Pieces, Weight, ChargeableWeight, Volume, Container, Calculated, FlatRate |
82
+ | `applyToContactId` | `int` | FK to Contact (who pays) |
83
+ | `paidAs` | `PaidAs` enum | Prepaid, Collect |
84
+ | `currencyId` | `int` | FK to Currency |
85
+ | `accountingItemId` | `int` | FK to AccountingItem |
86
+ | `salesTaxId` | `int?` | FK to SalesTax |
87
+ | `salesTaxRate` | `decimal` | |
88
+ | `salesTaxAmount` | `decimal` | Computed: `salesTaxRate * amount` |
89
+ | `totalAmount` | `decimal` | Computed: `amount + salesTaxAmount` |
90
+ | `showInDocuments` | `bool` | |
91
+ | `isConsolidated` | `bool` | |
92
+ | `allowAutomaticUpdate` | `bool` | |
93
+ | `note` | `string?` | |
94
+ | `rateId` | `int?` | FK to Rate |
95
+ | `customValues` | `Dictionary` | Own customValues |
96
+
97
+ **Navigation:** `applyToContact`, `currency`, `accountingItem`, `salesTax`, `rate`, `orders` (many-to-many)
98
+
99
+ ---
100
+
101
+ ## Payment Entity
102
+
103
+ | Field | Type | Notes |
104
+ |-------|------|-------|
105
+ | `paymentId` | `int` | Primary key |
106
+ | `organizationId` | `int` | |
107
+ | `divisionId` | `int` | |
108
+ | `applyToContactId` | `int` | FK to Contact |
109
+ | `amountReceived` | `decimal` | Total payment amount |
110
+ | `currencyId` | `int` | FK to Currency |
111
+ | `paymentDate` | `DateTime` | |
112
+ | `checkNumber` | `string` | |
113
+ | `memo` | `string` | |
114
+ | `accountingAccountId` | `int` | FK to AccountingAccount |
115
+ | `paymentStatus` | `PaymentStatus` enum | Posted=1, Void=2 |
116
+ | `customValues` | `Dictionary` | Own customValues |
117
+
118
+ **Navigation:** `applyToContact`, `currency`, `accountingAccount`, `division`, `organization`, `accountingTransactions` (many-to-many)
119
+
120
+ **Join entity `AccountingTransactionPayment`** has `amountApplied` field — the amount from this payment applied to a specific transaction.
121
+
122
+ ---
123
+
124
+ ## Enums
125
+
126
+ ### AccountingTransactionType
127
+ | Value | Int | Description |
128
+ |-------|-----|-------------|
129
+ | `Invoice` | 0 | Revenue — billed to customer |
130
+ | `Bill` | 1 | Expense — owed to vendor |
131
+ | `CreditMemo` | 2 | Credit adjustment |
132
+
133
+ ### AccountingTransactionStatus
134
+ `Open=0`, `Paid=1`, `Void=2`
135
+
136
+ ### ChargeType
137
+ `Income`, `Expense`, `Credit`
138
+
139
+ ### ChargeStatus
140
+ `Pending`, `Open`, `Posted`, `Paid`, `Void`
141
+
142
+ ### ApplyBy
143
+ `Pieces`, `Weight`, `ChargeableWeight`, `Volume`, `Container`, `Calculated`, `FlatRate`
144
+
145
+ ### PaymentStatus
146
+ `Posted=1`, `Void=2`
147
+
148
+ ### PaidAs
149
+ `Prepaid=0`, `Collect=1`
150
+
151
+ ---
152
+
153
+ ## CustomValues
154
+
155
+ `Dictionary<string, object?>` stored as PostgreSQL `jsonb`. Present on:
156
+ - `AccountingTransaction.customValues`
157
+ - `Charge.customValues`
158
+ - `Payment.customValues`
159
+
160
+ ```yaml
161
+ # Access in workflow expressions
162
+ invoiceNumber: "{{ entity.transactionNumber }}"
163
+ customerName: "{{ entity.applyToContact.name }}"
164
+ customField: "{{ entity.customValues.myField }}"
165
+
166
+ # Charge access via collection
167
+ chargeAmount: "{{ entity.charges[0].amount }}"
168
+
169
+ # NCalc conditions
170
+ conditions:
171
+ - expression: "[entity.accountingTransactionStatus] = 'Open'"
172
+ - expression: "[entity.amountDue] > 0"
173
+ ```
@@ -0,0 +1,205 @@
1
+ # Commodity Entity Field Reference
2
+
3
+ Field names as used in workflow expressions: `{{ entity.description }}`, `{{ entity.customValues.myField }}`.
4
+
5
+ ## Scalar Fields
6
+
7
+ | Field | Type | Notes |
8
+ |-------|------|-------|
9
+ | `commodityId` | `int` | Primary key |
10
+ | `organizationId` | `int` | Tenant scope |
11
+ | `description` | `string` | Required |
12
+ | `pieces` | `int` | Number of pieces |
13
+ | `quantity` | `int?` | Inner quantity per piece |
14
+ | `unit` | `string?` | Unit of measure (e.g., "TV", "Pallet", "Kg") |
15
+ | `note` | `string?` | |
16
+ | `serialNumber` | `string?` | Domain only (not in GraphQL) |
17
+ | `isDeleted` | `bool?` | Soft delete |
18
+
19
+ ### Weight Fields
20
+
21
+ | Field | Type | Notes |
22
+ |-------|------|-------|
23
+ | `weight` | `decimal?` | Per piece (or total if `weightByTotal`) |
24
+ | `weightTotal` | `decimal?` | Calculated: `weight * pieces` |
25
+ | `weightUnit` | `WeightUnit` enum | Lb, Kg |
26
+ | `weightByTotal` | `bool` | When true, weight = sum of children |
27
+
28
+ ### Dimension Fields
29
+
30
+ | Field | Type | Notes |
31
+ |-------|------|-------|
32
+ | `length` | `decimal?` | |
33
+ | `width` | `decimal?` | |
34
+ | `height` | `decimal?` | |
35
+ | `dimensionsUnit` | `DimensionsUnit` enum | In, Cm, M, Ft |
36
+
37
+ ### Volume Fields
38
+
39
+ | Field | Type | Notes |
40
+ |-------|------|-------|
41
+ | `volumePiece` | `decimal?` | Calculated: `L * W * H` converted to volumeUnit |
42
+ | `volumeTotal` | `decimal?` | Calculated: `pieces * volumePiece` |
43
+ | `volumeUnit` | `VolumeUnit` enum | Ft, Vlb, Vkg, M, In, Cm |
44
+
45
+ ### Value Fields
46
+
47
+ | Field | Type | Notes |
48
+ |-------|------|-------|
49
+ | `unitaryValue` | `decimal?` | Value per unit |
50
+ | `unitaryValueTotal` | `decimal?` | Calculated: `unitaryValue * quantity * pieces` |
51
+ | `valueByTotal` | `bool` | When true, value = sum of children |
52
+
53
+ ### Foreign Keys
54
+
55
+ | Field | Type | Notes |
56
+ |-------|------|-------|
57
+ | `commodityStatusId` | `int?` | FK to CommodityStatus |
58
+ | `commodityTypeId` | `int?` | FK to CommodityType |
59
+ | `packageTypeId` | `int?` | FK to PackageType |
60
+ | `warehouseLocationId` | `int?` | FK — cascades to children |
61
+ | `containerCommodityId` | `int?` | FK self-ref (parent commodity) |
62
+ | `jobId` | `Guid?` | FK to Job |
63
+ | `inventoryItemId` | `int?` | FK to InventoryItem |
64
+ | `billToContactId` | `int?` | FK to Contact |
65
+ | `created` | `DateTime` | |
66
+ | `createdBy` | `string` | User ID |
67
+ | `lastModified` | `DateTime` | |
68
+ | `lastModifiedBy` | `string` | User ID |
69
+
70
+ ## Navigation Properties
71
+
72
+ | Field | Type | Notes |
73
+ |-------|------|-------|
74
+ | `commodityStatus` | `CommodityStatus` | `.statusName`, `.statusStage` |
75
+ | `commodityType` | `CommodityType` | `.code`, `.description` |
76
+ | `packageType` | `PackageType` | `.name` |
77
+ | `warehouseLocation` | `WarehouseLocation` | |
78
+ | `containerCommodity` | `Commodity` | Parent commodity |
79
+ | `job` | `Job` | |
80
+ | `inventoryItem` | `InventoryItem` | |
81
+ | `billToContact` | `Contact` | |
82
+ | `createdUser` | `User` | |
83
+ | `updatedUser` | `User` | |
84
+
85
+ ## Collection Properties
86
+
87
+ | Field | Type | Notes |
88
+ |-------|------|-------|
89
+ | `containerCommodities` | `[Commodity]` | Child commodities in this container |
90
+ | `orderCommodities` | `[OrderCommodity]` | Join to orders (has own `customValues`) |
91
+ | `commodityTrackingNumbers` | `[CommodityTrackingNumber]` | Tracking numbers |
92
+ | `commodityTags` | `[CommodityTag]` | Tags (has own `customValues`) |
93
+ | `allTags` | `[CommodityAllTagsView]` | View: includes inherited tags |
94
+ | `trackingEvents` | `[TrackingEvent]` | |
95
+ | `shipments` | `[Order]` | Related orders (GraphQL) |
96
+
97
+ ## Computed/Resolved GraphQL Fields
98
+
99
+ | Field | Returns | Notes |
100
+ |-------|---------|-------|
101
+ | `totalAmount` | `decimal` | Computed: `unitaryValue * quantity` |
102
+ | `packageTypeName` | `string` | From packageType.name |
103
+ | `trackingNumbers` | `[TrackingNumber]` | From commodityTrackingNumbers |
104
+ | `getChildCommodities(filter)` | `[Commodity]` | Child commodities |
105
+ | `getParentCommodity` | `Commodity` | Parent commodity |
106
+ | `getRelatedOrders(filter)` | `[Order]` | Related orders |
107
+ | `getRelatedOrder(filter)` | `Order` | First related order |
108
+ | `getCommodityTrackingNumber(idPropertyName)` | `TrackingNumber` | Lookup |
109
+ | `getCommodityAttachments(filter)` | `[Attachment]` | |
110
+ | `changeHistory(startDate, endDate, maxResults)` | `[ChangeHistory]` | Audit trail |
111
+
112
+ ## Container/Child Pattern (Self-Referencing)
113
+
114
+ Commodities form a tree via `containerCommodityId`:
115
+
116
+ ```
117
+ Container Commodity (parent)
118
+ ├── Child Commodity 1
119
+ ├── Child Commodity 2
120
+ └── Child Commodity 3
121
+ ```
122
+
123
+ - Parent → children: `containerCommodities` collection
124
+ - Child → parent: `containerCommodity` navigation
125
+ - When `weightByTotal=true`, weight is computed as sum of children
126
+ - Changing warehouse location on parent cascades to children
127
+ - Changing commodity status on parent cascades to children
128
+ - Deleting parent cascades to children
129
+
130
+ ## CommodityTrackingNumber Sub-Entity
131
+
132
+ | Field | Type | Notes |
133
+ |-------|------|-------|
134
+ | `commodityTrackingNumberId` | `int` | PK |
135
+ | `trackingNumber` | `string` | |
136
+ | `trackingNumberType` | `string?` | BOL, PRO, PO, etc. |
137
+ | `isPrimary` | `bool` | |
138
+ | `syncOrderId` | `int?` | |
139
+
140
+ ## OrderCommodity Join Entity
141
+
142
+ Links commodities to orders with per-association metadata:
143
+
144
+ | Field | Type | Notes |
145
+ |-------|------|-------|
146
+ | `commodityId` | `int` | |
147
+ | `orderId` | `int` | |
148
+ | `customValues` | `Dictionary` | **Own** customValues, separate from Commodity |
149
+ | `commodity` | `Commodity` | Navigation |
150
+ | `order` | `Order` | Navigation |
151
+
152
+ ## Enums
153
+
154
+ ### WeightUnit
155
+ `Lb`, `Kg`
156
+
157
+ ### DimensionsUnit
158
+ `In`, `Cm`, `M`, `Ft`
159
+
160
+ ### VolumeUnit
161
+ | Value | Description |
162
+ |-------|-------------|
163
+ | `Ft` | Cubic feet |
164
+ | `Vlb` | Volumetric lbs |
165
+ | `Vkg` | Volumetric kg |
166
+ | `M` | Cubic meters |
167
+ | `In` | Cubic inches |
168
+ | `Cm` | Cubic cm |
169
+
170
+ ### CommodityStatusStage
171
+ `Pending=1`, `InProgress`, `Completed`
172
+
173
+ ### CommodityStatus (enum, distinct from entity)
174
+ `Pending`, `OnRoute`, `Delivered`, `Cancelled`, `InQuote`, `OnHand`, `OnPacking`, `InTransit`
175
+
176
+ ## CustomValues
177
+
178
+ `Dictionary<string, object?>` stored as PostgreSQL `jsonb`. Access in workflows:
179
+
180
+ ```yaml
181
+ # Template expressions
182
+ value: "{{ entity.description }}"
183
+ value: "{{ entity.customValues.myField }}"
184
+ value: "{{ entity.containerCommodity.description }}"
185
+
186
+ # Access weight with unit conversion
187
+ weight: "{{ entity.weight }}"
188
+
189
+ # NCalc conditions
190
+ conditions:
191
+ - expression: "[entity.pieces] > 0 AND isNullOrEmpty([entity.customValues.lotNumber?]) = false"
192
+
193
+ # Update via Commodity/Update task
194
+ inputs:
195
+ commodityId: "{{ entity.commodityId }}"
196
+ commodity:
197
+ CustomValues.lotNumber: "LOT-001"
198
+ CustomValues.hazmat: true
199
+ ```
200
+
201
+ **Entities with own customValues:**
202
+ - `Commodity.customValues` — commodity-level fields
203
+ - `OrderCommodity.customValues` — per-order-per-commodity fields
204
+ - `CommodityType.customValues` — type-level fields
205
+ - `CommodityTag.customValues` — tag-level fields
@@ -0,0 +1,153 @@
1
+ # Contact Entity Field Reference
2
+
3
+ Field names as used in workflow expressions: `{{ entity.name }}`, `{{ entity.customValues.myField }}`.
4
+
5
+ ## Scalar Fields
6
+
7
+ | Field | Type | Notes |
8
+ |-------|------|-------|
9
+ | `contactId` | `int` | Primary key |
10
+ | `organizationId` | `int` | Tenant scope |
11
+ | `name` | `string` | Required. Company/display name |
12
+ | `contactFirstName` | `string?` | |
13
+ | `contactLastName` | `string?` | |
14
+ | `contactType` | `ContactType` enum | See enum below |
15
+ | `accountNumber` | `string?` | |
16
+ | `emailAddress` | `string?` | |
17
+ | `phoneNumber` | `string?` | |
18
+ | `mobilePhoneNumber` | `string?` | |
19
+ | `faxNumber` | `string?` | |
20
+ | `website` | `string?` | |
21
+ | `idNumber` | `string?` | EIN, DUNS, etc. |
22
+ | `idNumberType` | `IDNumberType?` enum | EIN, DUNS, ForeignEntityId, Other |
23
+ | `divisionId` | `int` | Primary division FK |
24
+ | `paymentTermId` | `int?` | FK to PaymentTerm |
25
+ | `creditLimit` | `decimal?` | |
26
+ | `paidAs` | `PaidAs?` enum | Prepaid, Collect |
27
+ | `isACorporation` | `bool?` | |
28
+ | `isDeleted` | `bool` | Soft delete flag |
29
+ | `contactStatusId` | `int?` | FK to ContactStatus |
30
+ | `entityTypeId` | `int?` | FK to EntityType |
31
+ | `parentContactId` | `int?` | Self-referencing FK |
32
+ | `tags` | `[string]` | String array |
33
+ | `created` | `DateTime` | |
34
+ | `createdBy` | `string` | User ID |
35
+ | `lastModified` | `DateTime` | |
36
+ | `lastModifiedBy` | `string` | User ID |
37
+
38
+ ## Navigation Properties
39
+
40
+ | Field | Type | Notes |
41
+ |-------|------|-------|
42
+ | `division` | `Division` | Primary division |
43
+ | `paymentTerm` | `PaymentTerm` | |
44
+ | `contactStatus` | `ContactStatus` | `.statusName`, `.statusStage` (Active/Inactive) |
45
+ | `entityType` | `EntityType` | |
46
+ | `parentContact` | `Contact` | Self-referencing parent |
47
+ | `createdUser` | `User` | |
48
+ | `updatedUser` | `User` | |
49
+
50
+ ## Collection Properties
51
+
52
+ | Field | Type | Notes |
53
+ |-------|------|-------|
54
+ | `contactAddresses` | `[ContactAddress]` | Addresses with types (Billing, Shipping, Other) |
55
+ | `contacts` | `[Contact]` | Linked contacts (TO this contact) |
56
+ | `linksToContacts` | `[Contact]` | Contacts this one links TO |
57
+ | `contactLinks` | `[ContactLink]` | Link details (type: ParentContact, FactoringCompany, SalesPerson) |
58
+ | `equipmentTypes` | `[EquipmentType]` | Carrier only |
59
+ | `discounts` | `[Discount]` | Via ContactDiscount join |
60
+ | `rates` | `[Rate]` | Carrier rates |
61
+ | `additionalDivisions` | `[Division]` | Beyond primary division |
62
+ | `orders` | `[Order]` | Via OrderCarrier |
63
+
64
+ ## Computed/Resolved GraphQL Fields
65
+
66
+ | Field | Returns | Notes |
67
+ |-------|---------|-------|
68
+ | `getContactAddressByType(addressType)` | `[ContactAddress]` | Filter by "Billing", "Shipping", "Other" |
69
+ | `getFirstContactAddressByType(addressType)` | `ContactAddress` | First match |
70
+ | `getContactAttachments(filter, orderBy)` | `[Attachment]` | |
71
+ | `getCustomValuesAttachment(filter)` | `Attachment` | From customValues `attachmentId` key |
72
+ | `availableCredit` | `[AvailableCreditByCurrency]` | Carrier/Customer only |
73
+ | `changeHistory(startDate, endDate, maxResults)` | `[ChangeHistory]` | Audit trail |
74
+
75
+ ## ContactType Enum
76
+
77
+ | Value | Int | Notes |
78
+ |-------|-----|-------|
79
+ | `Customer` | 1 | Can have accounting |
80
+ | `Carrier` | 2 | Can have accounting, equipment types |
81
+ | `Vendor` | 3 | |
82
+ | `Contact` | 4 | Generic, must have linked contact |
83
+ | `Driver` | 5 | |
84
+ | `Employee` | 6 | Can have user account |
85
+ | `SalesPerson` | 7 | |
86
+ | `ForwardingAgent` | 8 | |
87
+ | `FactoringCompany` | 9 | |
88
+ | `Lead` | 10 | |
89
+ | `PoolPoint` | 11 | |
90
+ | `DistributionCenter` | 12 | |
91
+ | `Store` | 13 | |
92
+ | `ContactUser` | 14 | User associated with contact |
93
+ | `USPPI` | 15 | US Principal Party in Interest |
94
+
95
+ ## ContactAddress Sub-Entity
96
+
97
+ | Field | Type | Notes |
98
+ |-------|------|-------|
99
+ | `contactAddressId` | `int` | PK |
100
+ | `addressLine` | `string?` | |
101
+ | `addressLine2` | `string?` | |
102
+ | `addressType` | `AddressType` enum | Billing=1, Shipping=2, Other=3 |
103
+ | `cityName` | `string?` | |
104
+ | `countryCode` | `string?` | FK to Country |
105
+ | `stateCode` | `string?` | FK to State |
106
+ | `postalCode` | `string?` | |
107
+ | `isInactive` | `bool?` | |
108
+ | `latitude` | `double?` | From Location.Y (GraphQL resolved) |
109
+ | `longitude` | `double?` | From Location.X (GraphQL resolved) |
110
+ | `customValues` | `Dictionary` | Own customValues (separate from Contact) |
111
+ | `country` | `Country` | Navigation |
112
+ | `state` | `State` | Navigation |
113
+
114
+ GraphQL resolver: `formattedAddress(outputFormat, addressFormat, lang, multiline)` — formatted string.
115
+
116
+ ## Other Related Enums
117
+
118
+ | Enum | Values |
119
+ |------|--------|
120
+ | `IDNumberType` | EIN, DUNS, ForeignEntityId, Other |
121
+ | `PaidAs` | Prepaid, Collect |
122
+ | `AddressType` | Billing=1, Shipping=2, Other=3 |
123
+ | `ContactLinkType` | ParentContact=1, FactoringCompany, SalesPerson, ContactAddressLink |
124
+ | `ContactStatusStage` | Active=0, Inactive=1 |
125
+ | `PaymentType` | Card=1, AccountCredit=2, Cash=3, Check=4, BankTransfer=5, Other=7 |
126
+
127
+ ## CustomValues
128
+
129
+ `Dictionary<string, object?>` stored as PostgreSQL `jsonb`. Access in workflows:
130
+
131
+ ```yaml
132
+ # Template expressions
133
+ value: "{{ entity.name }}"
134
+ value: "{{ entity.customValues.myField }}"
135
+
136
+ # Access contact address
137
+ address: "{{ entity.contactAddresses[0].addressLine }}"
138
+
139
+ # NCalc conditions
140
+ conditions:
141
+ - expression: "[entity.contactType] = 'Customer'"
142
+
143
+ # Update via Contact/Update task
144
+ inputs:
145
+ contactId: "{{ entity.contactId }}"
146
+ contact:
147
+ CustomValues.myField: "newValue"
148
+ ```
149
+
150
+ **Entities with own customValues:**
151
+ - `Contact.customValues` — contact-level custom fields
152
+ - `ContactAddress.customValues` — address-level custom fields
153
+ - `ContactPaymentMethod.customValues` — payment method custom fields