Job
Introduction
The Job entity is an aggregate root that serves as a container for grouping multiple related orders, commodities, and accounting transactions. Jobs enable consolidation scenarios where several orders need to be tracked together, such as master bill consolidations, project-based shipments, or multi-leg freight movements.
Unlike Order which uses integer IDs, Job uses a Guid (GUID/UUID) as its primary identifier, providing globally unique identification suitable for distributed systems and external integrations. Jobs support draft mode, custom values for extensibility, and maintain relationships to customers, employees, and organizational divisions.
Entity Structure
Properties
| Property Name | Type | Required | Description |
|---|---|---|---|
| JobId | Guid | Yes | Globally unique identifier (GUID/UUID) - primary key |
| JobNumber | string | Yes | Human-readable job number (unique within organization) |
| OrganizationId | int | Yes | Organization owning this job (multi-tenancy) |
| DivisionId | int? | No | Division within organization for data partitioning |
| CustomerId | int? | No | Customer contact for this job |
| Customer | Contact | No | Navigation property to customer contact |
| EmployeeId | int? | No | Employee responsible for this job |
| Employee | Contact | No | Navigation property to employee contact |
| JobStatusId | int? | No | Current status of the job |
| JobStatus | JobStatus | No | Navigation property to status definition |
| Description | string? | No | Job description or notes |
| IsDraft | bool | Yes | Whether job is in draft state (default: false) |
| CustomValues | Dictionary<string, object?> | No | Extensible custom properties dictionary |
| Created | DateTime | Yes | Creation timestamp (inherited from AuditableEntity) |
| CreatedBy | string | Yes | User ID who created the job (inherited) |
| LastModified | DateTime | Yes | Last modification timestamp (inherited) |
| LastModifiedBy | string | Yes | User ID who last modified (inherited) |
Relationships (Navigation Properties)
| Relationship | Type | Description |
|---|---|---|
| JobOrders | ICollection<JobOrder> | Junction table to orders in this job |
| Orders | ICollection<Order> | Orders grouped in this job (via JobOrders) |
| JobAccountingTransactions | ICollection<JobAccountingTransaction> | Junction table to accounting transactions |
| AccountingTransactions | ICollection<AccountingTransaction> | Transactions associated with this job |
| Commodities | ICollection<Commodity> | Commodities directly associated with job |
| Division | Division | Parent division |
| Customer | Contact | Customer for this job |
| Employee | Contact | Employee managing this job |
| JobStatus | JobStatus | Current job status |
Key Differences from Order
| Aspect | Job | Order |
|---|---|---|
| Primary Key | Guid (GUID/UUID) | int |
| Purpose | Group multiple orders/transactions | Single operational shipment |
| Aggregate Scope | Multiple orders + transactions | Single order with commodities/charges |
| Status Management | JobStatus (simpler) | OrderStatus (lifecycle-driven) |
| Domain Events | None currently | OrderStatusChangedEvent |
YAML Configuration
Creating a Job
# Create a new job for consolidation
- task: Job/Create
name: createConsolidationJob
inputs:
organizationId: ${organizationId}
job:
jobNumber: "JOB-2025-00123"
divisionId: ${divisionId}
customerId: ${customerId}
employeeId: ${employeeId}
jobStatusId: ${activeJobStatusId}
description: "Shanghai to Los Angeles Consolidation - Jan 2025"
isDraft: false
customValues:
projectCode: "PROJ-ASIA-001"
containerType: "40HC"
estimatedCommodities: 45
notes: "Temperature controlled shipment"
outputs:
- job: newJob
Adding Orders to a Job
# Associate existing orders with a job
- task: Job/Update
name: assignOrdersToJob
inputs:
organizationId: ${organizationId}
jobId: ${jobId}
job:
jobOrders:
- orderId: ${order1Id}
- orderId: ${order2Id}
- orderId: ${order3Id}
- orderId: ${order4Id}
Adding Accounting Transactions to Job
# Link invoices and bills to the job
- task: Job/Update
name: linkInvoicesToJob
inputs:
organizationId: ${organizationId}
jobId: ${jobId}
job:
jobAccountingTransactions:
- accountingTransactionId: ${masterInvoiceId}
- accountingTransactionId: ${freightBillId}
- accountingTransactionId: ${customsBillId}
Querying Job with All Relationships
# Query job with complete data using GraphQL
- task: Query/GraphQL
name: getFullJobData
inputs:
organizationId: ${organizationId}
query: |
query GetJob($jobId: ID!) {
job(id: $jobId) {
jobId
jobNumber
description
jobOrders {
order {
orderNumber
orderCommodities {
commodity { description commodityType { name } }
}
orderStatus { statusName }
billToContact { name }
}
}
jobAccountingTransactions {
accountingTransaction {
accountingTransactionNumber
charges { description amount }
accountingItem { code name }
}
}
commodities { description commodityType { name } commodityStatus { statusName } }
customer { name contactAddresses { address1 city } }
employee { name }
division { divisionName }
jobStatus { statusName }
}
}
variables:
jobId: ${jobId}
outputs:
- response.job: fullJob
Updating Job Properties
# Update job description and status
- task: Job/Update
name: updateJobInfo
inputs:
organizationId: ${organizationId}
jobId: ${jobId}
job:
description: "Updated: Container departed Shanghai on 2025-01-15"
jobStatusId: ${inTransitStatusId}
Working with Custom Values
# Add/update custom job properties
- task: Job/Update
name: updateJobCustomValues
inputs:
organizationId: ${organizationId}
jobId: ${jobId}
job:
customValues:
masterBL: "MAEU1234567890"
vesselName: "MAERSK ESSEX"
voyageNumber: "V2025-001W"
ETD: "2025-01-15"
ETA: "2025-02-05"
totalCBM: 67.5
totalGrossWeight: 12500
consolidationType: "FCL"
actualArrivalDate: "2025-02-04"
Draft Job Workflow
# Step 1: Create draft job for planning
- task: Job/Create
name: createDraftJob
inputs:
organizationId: ${organizationId}
job:
jobNumber: "JOB-DRAFT-${timestamp}"
customerId: ${customerId}
isDraft: true
description: "Planning consolidation - not yet confirmed"
outputs:
- job: draftJob
# Step 2: Add tentative orders
- task: Job/Update
name: addTentativeOrders
inputs:
organizationId: ${organizationId}
jobId: ${draftJob.jobId}
job:
jobOrders: ${tentativeOrderIds}
# Step 3: Later, finalize the job
- task: Job/Update
name: finalizeJob
inputs:
organizationId: ${organizationId}
jobId: ${draftJob.jobId}
job:
isDraft: false
jobNumber: "JOB-2025-${finalSequence}"
Key Methods
The Job entity provides methods for managing its aggregate:
Order Management
ChangeJobOrders(List<int>)- Set orders in job (adds new, removes unspecified)
Accounting Transaction Management
ChangeJobAccountingTransactions(List<int>)- Set accounting transactions (adds new, removes unspecified)
Property Updates
ChangeJobNumber(string)- Update job numberChangeCustomerId(int?)- Change customerChangeEmployeeId(int?)- Assign employeeChangeDivisionId(int?)- Change divisionChangeJobStatusId(int?)- Update job statusChangeDescription(string?)- Update descriptionChangeIsDraft(bool)- Toggle draft mode
Custom Values Management
ChangeCustomValues(Dictionary<string, object?>)- Bulk update custom valuesChangeCustomValue(string, object?)- Update single custom value (removes if value is null)