Skip to main content

Order

Introduction

The Order entity is one of the most critical aggregate roots in CXTMS, representing shipments, quotes, warehouse bookings, and various other order types. As an aggregate root, Order controls all related child entities including commodities, charges, tracking events, documents, and entity relationships (shipper, consignee, carriers).

Orders serve as the primary operational unit in the TMS, tracking the entire lifecycle of freight movements from quotation through delivery. They integrate with the Job entity for multi-order consolidation, the accounting system for invoicing, and the workflow engine for automated business processes.

Entity Structure

Properties

Property NameTypeRequiredDescription
OrderIdintYesUnique identifier for the order (primary key)
OrderNumberstringYesHuman-readable order number (unique within organization)
OrganizationIdintYesOrganization owning this order (multi-tenancy)
DivisionIdintYesDivision within organization for data partitioning
OrderTypeOrderTypesYesType of order (see OrderTypes enum below)
OrderStatusIdintYesCurrent status of the order
OrderStatusOrderStatusNoNavigation property to status definition
BillToContactIdint?NoContact to bill for this order
BillToContactContactNoNavigation property to billing contact
EmployeeContactIdint?NoEmployee responsible for this order
EmployeeContactContactNoNavigation property to employee
SalespersonContactIdint?NoSalesperson who sold this order
SalespersonContactContactNoNavigation property to salesperson
EquipmentTypeIdint?NoDeprecated - Equipment type for this order
EquipmentTypeEquipmentTypeNoNavigation property to equipment type
TrackingNumberstring?NoPrimary tracking number for the shipment
IsDraftboolYesWhether order is in draft state (default: false)
LastOrderStatusModifiedDateTime?NoTimestamp when status was last changed
CustomValuesDictionary<string, object?>NoExtensible custom properties dictionary
SearchVectorNpgsqlTsVectorNoFull-text search vector (PostgreSQL)
ThirdPartyContactBillingContactNoThird-party billing contact (not mapped)
CreatedDateTimeYesCreation timestamp (inherited from AuditableEntity)
CreatedBystringYesUser ID who created the order (inherited)
LastModifiedDateTimeYesLast modification timestamp (inherited)
LastModifiedBystringYesUser ID who last modified (inherited)

Relationships (Navigation Properties)

RelationshipTypeDescription
OrderCommoditiesICollection<OrderCommodity>Junction to commodities on this order
OrderChargesICollection<OrderCharges>Junction to charges applied to this order
OrderEntitiesICollection<OrderEntity>Dynamic entities (shipper, consignee, notify party, etc.)
OrderCarriersICollection<OrderCarrier>Deprecated - Junction to carrier contacts (use OrderEntities instead)
OrderDocumentsICollection<OrderDocument>Documents attached to this order
OrderEventsICollection<OrderEvents>Order event history
OrderTagsICollection<OrderTag>Tags assigned to this order
TrackingEventsICollection<TrackingEvent>Shipment tracking events
JobsICollection<Job>Jobs that include this order
OrganizationOrganizationParent organization
DivisionDivisionParent division
ChargesICollection<Charge>Direct charges collection
CarriersICollection<Contact>Deprecated - Carrier contacts collection (use OrderEntities instead)
AllTagsICollection<OrderAllTagsView>View of all tags (flattened)
AllRelatedOrdersICollection<OrderRelatedOrdersView>View of related orders

OrderTypes Enum

The OrderTypes enum defines the various order classifications:

ValueNameDescription
0OrderGeneric order type
1QuoteQuotation/rate request
2WarehouseReceiptWarehouse order/receipt
3PurchasePickup order (purchase)
4ParcelShipmentParcel/small package shipment
5AirShipmentOrderAir freight shipment
6OceanShipmentOrderOcean freight shipment
7CargoMovementCargo movement operation
8EntityTypeEntity type-driven order
9PickupOrderPickup order
10LoadOrderLoading operation
11BookingOrderBooking request
12FreightGeneral freight order
13DeliveryOrderDelivery operation

YAML Configuration

Creating an Order

# Create a new ocean shipment order
- task: Order/Create@1
name: createOceanShipment
inputs:
organizationId: ${organizationId}
order:
orderNumber: "OCN-2025-00123"
orderType: OceanShipmentOrder
divisionId: ${divisionId}
billToContactId: ${customerId}
employeeContactId: ${employeeId}
salespersonContactId: ${salespersonId}
orderStatusId: ${newStatusId}
trackingNumber: "MAEU12345678"
isDraft: false
customValues:
customerReference: "PO-98765"
specialInstructions: "Temperature controlled"
incoterms: "FOB"
serviceLevel: "Express"
outputs:
- order: newOrder

Adding Commodities to Order

# Create order
- task: Order/Create@1
name: createAirShipment
inputs:
organizationId: ${organizationId}
order:
orderNumber: "AIR-2025-00456"
orderType: AirShipmentOrder
billToContactId: ${customerId}
divisionId: ${divisionId}
orderStatusId: ${activeStatusId}
outputs:
- order: order

# Add first commodity to order
- task: OrderCommodity/Create
name: addCommodity1
inputs:
organizationId: ${organizationId}
orderCommodity:
orderId: ${order.orderId}
commodityId: ${commodity1Id}
customValues:
sequence: 1
handling: "Fragile"

# Add second commodity to order
- task: OrderCommodity/Create
name: addCommodity2
inputs:
organizationId: ${organizationId}
orderCommodity:
orderId: ${order.orderId}
commodityId: ${commodity2Id}
customValues:
sequence: 2
handling: "Standard"

Setting Order Entities (Shipper/Consignee)

# Update order with shipper, consignee, and notify party
- task: Order/Update@1
name: setOrderEntities
inputs:
organizationId: ${organizationId}
orderId: ${orderId}
order:
orderEntities:
- entityType: "Shipper"
contactId: ${shipperContactId}
contactAddressId: ${shipperAddressId}
orderEntitySequence: 1
- entityType: "Consignee"
contactId: ${consigneeContactId}
contactAddressId: ${consigneeAddressId}
orderEntitySequence: 2
- entityType: "NotifyParty"
contactId: ${notifyContactId}
orderEntitySequence: 3
- entityType: "ForwardingAgent"
nonContactName: "Local Customs Broker"
customValues:
phone: "+1-555-0123"
email: "[email protected]"
orderEntitySequence: 4

Querying Order with Relationships

# Query order with all related data using GraphQL
- task: Query/GraphQL
name: getFullOrder
inputs:
organizationId: ${organizationId}
query: |
query GetOrder($orderId: Int!) {
order(id: $orderId) {
orderId
orderNumber
orderType
orderCommodities {
commodity {
commodityId
description
commodityType { name }
commodityTrackingNumbers { trackingNumber }
}
}
orderCharges {
charge {
chargeId
description
amount
accountingItem { code name }
}
}
orderEntities {
entityType
contact {
name
contactAddresses { address1 city state }
}
}
orderCarriers { contact { name } }
orderDocuments { fileName fileUrl }
orderTags { tag { name } }
trackingEvents {
eventDate
eventDefinition { name }
}
billToContact { name }
employeeContact { name }
salespersonContact { name }
orderStatus { statusName }
division { divisionName }
}
}
variables:
orderId: ${orderId}
outputs:
- response.order: fullOrder

Updating Order Status

# Change order status (triggers OrderStatusChangedEvent)
- task: Order/Update@1
name: updateOrderStatus
inputs:
organizationId: ${organizationId}
orderId: ${orderId}
order:
orderStatusId: ${deliveredStatusId}

Working with Custom Values

# Add/update custom values on an order
- task: Order/Update@1
name: updateCustomValues
inputs:
organizationId: ${organizationId}
orderId: ${orderId}
order:
customValues:
customerPO: "PO-2025-ABC"
projectCode: "PROJ-789"
specialHandling: true
requiredDeliveryDate: "2025-02-15"
insuranceValue: 50000
temperatureRange: "-20°C to -18°C"

Cloning an Order

# Clone an existing order as a new quote
# Note: Clone method needs to be wrapped in Order/Update or custom workflow task
- task: Order/Get
name: getOriginalOrder
inputs:
organizationId: ${organizationId}
orderId: ${originalOrderId}
outputs:
- order: originalOrder

- task: Order/Create@1
name: createClonedOrder
inputs:
organizationId: ${organizationId}
order:
orderNumber: "QUOTE-${timestamp}"
orderType: Quote
# Copy relevant fields from originalOrder
billToContactId: ${originalOrder.billToContactId}
divisionId: ${originalOrder.divisionId}
customValues: ${originalOrder.customValues}
outputs:
- order: clonedOrder

Domain Events

The Order entity publishes the following domain events:

OrderStatusChangedEvent

Published when ChangeOrderStatusId() or ChangeOrderStatus() methods detect a status change.

Event Data:

  • Order entity with updated status
  • Previous status (implicit - not in current event)
  • New status (OrderStatusId, OrderStatus)
  • Timestamp (LastOrderStatusModified)

Use Cases:

  • Trigger email notifications on status changes
  • Update external systems when order reaches specific statuses
  • Log status history for audit trail
  • Initiate downstream workflows (e.g., invoicing on "Delivered")

Example Workflow Trigger:

trigger: DomainEvent
eventType: OrderStatusChangedEvent
tasks:
- task: Email/Send
name: sendDeliveryNotification
conditions: ${event.order.orderStatus.name == "Delivered"}
inputs:
to: ${event.order.billToContact.email}
subject: "Shipment Delivered - ${event.order.orderNumber}"
templateId: ${orderDeliveredTemplateId}
variables:
order: ${event.order}
deliveryDate: ${event.order.lastOrderStatusModified}

Key Methods

The Order entity provides rich business logic through encapsulated methods:

Commodity Management

  • ChangeOrderCommodities(IEnumerable<OrderCommodity>) - Replace commodity list with intelligent merging
  • AppendOrderCommodities(IEnumerable<OrderCommodity>) - Add commodities without removing existing
  • ChangeCommodities(IEnumerable<Commodity>) - Update commodities directly
  • ChangeOrderCommoditiesCustomValues(IEnumerable<OrderCommodity>) - Update custom values only
  • AddCommodity(Commodity) - Add single commodity to order

Entity Roles Management

  • ChangeOrderEntities(IEnumerable<Dictionary<string, object>>) - Update from dictionaries
  • SetOrderEntities(IEnumerable<OrderEntity>) - Set entities with intelligent merging
  • AddOrderEntity(OrderEntity) - Add single entity role

Charge Management

  • ChangeOrderCharges(IEnumerable<OrderCharges>) - Update charges with merging
  • AddCharge(Charge) - Add single charge to order

Carrier Management (Deprecated - Use OrderEntity instead)

  • ChangeOrderCarriers(IEnumerable<OrderCarrier>) - Deprecated - Update carrier assignments
  • ChangeCarriers(IEnumerable<Contact>) - Deprecated - Set carrier contacts directly

Tracking Management

  • ChangeEvents(IEnumerable<TrackingEvent>) - Update tracking events with merging
  • AddTrackingEvent(TrackingEvent) - Add single tracking event

Tag Management

  • ChangeOrderTags(List<int>) - Update tags by tag IDs
  • ChangeOrderTags(List<Tag>) - Update tags using Tag entities (supports unsaved tags)

Status Management

  • ChangeOrderStatusId(int) - Update status by ID (publishes domain event)
  • ChangeOrderStatus(OrderStatus) - Update status by entity (publishes domain event)

Property Updates

  • ChangeOrderNumber(string)
  • ChangeOrderType(OrderTypes)
  • ChangeBillToContactId(int?)
  • ChangeEmployeeContactId(int?)
  • ChangeSalespersonContactId(int?)
  • ChangeDivisionId(int)
  • ChangeTrackingNumber(string?)
  • ChangeIsDraft(bool)
  • ChangeCustomValues(Dictionary<string, object?>)

Utility Methods

  • Clone(OrderTypes) - Create a copy of the order with new type

Aggregate Boundary

As an aggregate root, Order enforces the following boundaries:

Direct Children (managed by Order):

  • OrderCommodity - Must be added/removed through Order methods
  • OrderCharge - Managed through Order's charge methods
  • OrderEntity - Controlled via Order's entity methods
  • OrderCarrier - Deprecated - Updated through Order's carrier methods (use OrderEntity instead)
  • OrderDocument - Managed through Order
  • OrderTag - Controlled via tag methods

External References (independent entities):

  • Contact (BillTo, Employee, Salesperson) - Referenced but not owned
  • Commodity - Referenced through OrderCommodity junction
  • Charge - Referenced through OrderCharges junction
  • OrderStatus - Reference data
  • Division, Organization - Organizational structure

Consistency Rules:

  • Always modify child entities through Order's methods, not directly
  • Status changes must go through ChangeOrderStatusId() to trigger events
  • Use SetOrderEntities() for intelligent merging of entity roles
  • Tags support both saved (TagId > 0) and unsaved (Tag entity) scenarios

Examples

Complete Order Creation Workflow

# Step 1: Create draft order
- task: Order/Create@1
name: createDraftOrder
inputs:
organizationId: ${organizationId}
order:
orderNumber: "ORD-2025-${sequenceNumber}"
orderType: OceanShipmentOrder
divisionId: ${divisionId}
billToContactId: ${customerId}
employeeContactId: ${currentUserId}
orderStatusId: ${draftStatusId}
isDraft: true
customValues:
origin: "Shanghai, China"
destination: "Los Angeles, USA"
estimatedShipDate: "2025-02-01"
outputs:
- order: newOrder

# Step 2: Add shipper and consignee
- task: Order/Update@1
name: setShipperConsignee
inputs:
organizationId: ${organizationId}
orderId: ${newOrder.orderId}
order:
orderEntities:
- entityType: "Shipper"
contactId: ${shipperContactId}
contactAddressId: ${shipperAddressId}
- entityType: "Consignee"
contactId: ${consigneeContactId}
contactAddressId: ${consigneeAddressId}

# Step 3: Add commodities to order
- task: OrderCommodity/Create
name: linkCommodities
collection: ${commodityList}
inputs:
organizationId: ${organizationId}
orderCommodity:
orderId: ${newOrder.orderId}
commodityId: ${item.commodityId}

# Step 4: Add carrier
- task: Order/Update@1
name: assignCarrier
inputs:
organizationId: ${organizationId}
orderId: ${newOrder.orderId}
order:
orderCarriers:
- carrierId: ${mainCarrierId}

# Step 5: Finalize order and activate
- task: Order/Update@1
name: finalizeOrder
inputs:
organizationId: ${organizationId}
orderId: ${newOrder.orderId}
order:
isDraft: false
orderStatusId: ${activeStatusId}

Order with Quote-to-Order Conversion

# Step 1: Create initial quote
- task: Order/Create@1
name: createQuote
inputs:
organizationId: ${organizationId}
order:
orderNumber: "QTE-${sequenceNumber}"
orderType: Quote
billToContactId: ${prospectId}
divisionId: ${divisionId}
orderStatusId: ${quoteStatusId}
customValues:
validUntil: "2025-02-28"
quotedRate: 2500
outputs:
- order: quote

# Step 2: Later, retrieve quote for conversion
- task: Order/Get
name: getQuote
inputs:
organizationId: ${organizationId}
orderId: ${quote.orderId}
outputs:
- order: quoteData

# Step 3: Create new shipment order from quote
- task: Order/Create@1
name: convertToShipment
inputs:
organizationId: ${organizationId}
order:
orderNumber: "ORD-${sequenceNumber}"
orderType: OceanShipmentOrder
billToContactId: ${quoteData.billToContactId}
divisionId: ${quoteData.divisionId}
orderStatusId: ${newOrderStatusId}
customValues:
convertedFromQuote: ${quoteData.orderNumber}
quotedRate: ${quoteData.customValues.quotedRate}
bookingNumber: "BKG-123456"
vesselName: "MAERSK ESSEX"
voyageNumber: "V2025-01"
outputs:
- order: shipmentOrder

Best Practices

1. Use Appropriate Order Types

  • Use Quote for rate requests and quotations
  • Use OceanShipmentOrder or AirShipmentOrder for actual shipments
  • Use WarehouseReceipt for warehouse operations
  • Use generic Order type sparingly - prefer specific types

2. Leverage Custom Values

  • Store customer-specific data (PO numbers, project codes) in customValues
  • Use custom values for flexible properties that vary by customer or order type
  • Avoid custom values for data requiring indexing or complex queries

3. Manage Aggregate Boundaries

  • Always add/update commodities through Order's methods (ChangeOrderCommodities())
  • Don't create OrderCommodity entities directly - use Order as entry point
  • Use SetOrderEntities() for intelligent merging of entity roles

4. Handle Draft Orders

  • Create orders with isDraft: true while collecting information
  • Finalize with ChangeIsDraft(false) before activating
  • Draft orders can be partially complete without validation errors

5. Status Change Events

  • Subscribe to OrderStatusChangedEvent for workflow automation
  • Use events for email notifications, external system updates, and audit logging
  • Status timestamp is automatically maintained in LastOrderStatusModified

6. Tracking Numbers

  • Use Order's TrackingNumber for primary/master tracking number
  • Use CommodityTrackingNumber entities for house bills and multiple tracking numbers
  • Update tracking numbers through ChangeTrackingNumber() for trim/validation

7. Entity Roles (Shipper/Consignee/Carrier)

  • Use OrderEntity for all party roles including shipper, consignee, notify party, and carriers
  • Avoid deprecated OrderCarrier - use OrderEntity with appropriate EntityType instead
  • Support non-contact entities using NonContactName for adhoc parties
  • Maintain sequence with OrderEntitySequence for display order
  • SetOrderEntities() provides intelligent merging and update logic

8. Performance Optimization

  • Use selective include to load only needed relationships
  • Avoid loading AllTags and AllRelatedOrders views unless necessary
  • Query with filters on indexed fields (OrderNumber, OrganizationId, DivisionId)

9. Multi-Tenancy

  • Always set OrganizationId - required for data isolation
  • Use DivisionId for sub-organizational partitioning
  • Queries automatically filter by organization context

10. Cloning Orders

  • Use Clone() for quote-to-order conversion
  • Clone method copies core properties and relationships
  • Update type-specific properties after cloning