Skip to content

Helper Functions Reference

Programmatic helper functions for order management, hierarchy operations, validation, and compensation - reusable across manual operations and pipeline automation.

Helper Functions

Complete set of helper functions for order operations:

  • Hierarchy Management - Create children, update ancestors, query descendants
  • Lifecycle Validation - Check completion/cancellation eligibility
  • Process-Stage Validation - Verify stage transitions and flows
  • Compensation Execution - Rollback, cleanup, and recovery
  • Utility Functions - Common operations and helpers
  • Shared by All - Used by manual routes, pipelines, and custom code

Integration: Import from #lib/order/helpers and #lib/order/descendants

Overview

Helper functions provide the core business logic for the order system. They're framework-agnostic - usable in API routes, pipeline stages, background jobs, or custom applications.

Why Helper Functions?

Consistency:

  • Same validation logic everywhere
  • Manual operations use same rules as pipelines
  • No duplicate code

Reusability:

  • Use in routes, pipelines, scripts
  • Compose complex operations
  • Build on validated primitives

Maintainability:

  • Update logic in one place
  • All consumers get fixes
  • Clear separation of concerns

Hierarchy Management

Located in: #lib/order/helpers and #lib/order/descendants

createChildOrder

Create a child order with full hierarchy and composition metadata.

Function Signature:

typescript
async function createChildOrder(
  params: CreateChildOrderParams,
  db: Db,
  context: Context
): Promise<CreateChildOrderResult>

Parameters:

typescript
interface CreateChildOrderParams {
  // Parent info
  parentOrder: BaseOrder
  parentOrderType: string
  
  // Child info
  childOrderType: string
  childOrderId: string
  childOrderReference: string
  lspOrderType: LSPOrderType
  processor: Context
  carrierTrackingNumber?: string
  
  // Composition metadata
  creationReason: 'LIFECYCLE_REQUIREMENT' | 'PIPELINE_STAGE' | 
                  'AUTOMATIC_RULE' | 'CONDITIONAL_TRIGGER' |
                  'EXCEPTION_HANDLING' | 'MANUAL_REQUEST' |
                  'COMPENSATION' | 'SPLIT_ORDER'
  created: ActionRecord
  relationship: {
    type: 'MANDATORY' | 'OPTIONAL' | 'CONDITIONAL'
    canBlockParent: boolean
    sequence?: number
    dependencies?: string[]
  }
  metadata?: {
    trigger?: string
    notes?: string
    tags?: string[]
  }
}

Returns:

typescript
interface CreateChildOrderResult {
  hierarchy: OrderHierarchy
  tracking: OrderTracking
  error?: string
}

Example Usage:

typescript
import { createChildOrder } from '#lib/order/helpers'

const result = await createChildOrder({
  parentOrder: outboundOrder,
  parentOrderType: 'OutboundOrder',
  childOrderType: 'InternalOrder',
  childOrderId: 'task-123',
  childOrderReference: 'PICK-001',
  lspOrderType: 'InternalOrder',
  processor: context,
  
  creationReason: 'PIPELINE_STAGE',
  created: {
    by: { type: 'PIPELINE', id: 'pipe-001', name: 'Outbound Pipeline' },
    at: { timestamp: Date.now(), timezone: 'UTC' }
  },
  relationship: {
    type: 'MANDATORY',
    canBlockParent: true
  }
}, db, context)

if (result.error) {
  console.error(result.error)
  // "Invalid order relationship: InternalOrder cannot spawn OutboundOrder"
} else {
  // Use result.hierarchy and result.tracking for new child
}

What It Does:

  1. ✅ Validates parent-child relationship
  2. ✅ Validates hierarchy depth
  3. ✅ Generates hierarchical tracking number
  4. ✅ Builds child hierarchy object
  5. ✅ Adds child to parent's childOrders array
  6. ✅ Updates all ancestors' descendant lists
  7. ✅ Returns hierarchy and tracking for child

addChildToParent

Add a child record to parent's childOrders array.

Function Signature:

typescript
async function addChildToParent(
  db: Db,
  context: Context,
  parentReference: string,
  childRecord: ChildOrderRecord
): Promise<void>

Example Usage:

typescript
import { addChildToParent } from '#lib/order/descendants'

await addChildToParent(
  db,
  context,
  'OUT-001',
  {
    orderId: 'task-123',
    orderReference: 'PICK-001',
    orderType: 'InternalOrder',
    lspOrderType: 'InternalOrder',
    processor: context,
    status: 'PENDING',
    created: { by: { type: 'USER', id: 'OPR-123' }, at: {...} },
    creationReason: 'MANUAL_REQUEST',
    parentStateAtCreation: { process: 'FULFILLMENT', stage: 'ALLOCATION', status: 'IN_PROGRESS' },
    relationship: { type: 'MANDATORY', canBlockParent: true }
  }
)

updateAncestorsDescendants

Update all ancestors with new/removed descendant.

Function Signature:

typescript
async function updateAncestorsDescendants(
  db: Db,
  context: Context,
  parentOrder: BaseOrder,
  action: 'ADD' | 'REMOVE',
  descendantReference: string,
  descendantType: string
): Promise<void>

Example Usage:

typescript
import { updateAncestorsDescendants } from '#lib/order/descendants'

// Add descendant to all ancestors
await updateAncestorsDescendants(
  db,
  context,
  parentOrder,
  'ADD',
  'PICK-001',
  'InternalOrder'
)

// Updates:
// - parent.allDescendantReferences += 'PICK-001'
// - grandparent.allDescendantReferences += 'PICK-001'
// - root.allDescendantReferences += 'PICK-001'
// - All get descendantCount incremented
// - All get descendantsByType.InternalOrder += 'PICK-001'

getAllDescendants

Get all descendant references (single query).

Function Signature:

typescript
async function getAllDescendants(
  db: Db,
  context: Context,
  orderReference: string
): Promise<string[]>

Example Usage:

typescript
import { getAllDescendants } from '#lib/order/descendants'

const descendants = await getAllDescendants(db, context, 'OUT-001')
// Returns: ['PICK-001', 'PACK-001', 'SHIP-001']

getAllDescendantOrders

Get all descendant orders (bulk fetch).

Function Signature:

typescript
async function getAllDescendantOrders(
  db: Db,
  context: Context,
  orderReference: string
): Promise<BaseOrder[]>

Example Usage:

typescript
import { getAllDescendantOrders } from '#lib/order/descendants'

const orders = await getAllDescendantOrders(db, context, 'OUT-001')
// Returns: Full order objects for all descendants

getDescendantsByType

Get descendants filtered by order type.

Function Signature:

typescript
async function getDescendantsByType(
  db: Db,
  context: Context,
  orderReference: string,
  orderType: string
): Promise<string[]>

Example Usage:

typescript
import { getDescendantsByType } from '#lib/order/descendants'

const tasks = await getDescendantsByType(db, context, 'OUT-001', 'InternalOrder')
// Returns: ['PICK-001', 'PACK-001']

const shipping = await getDescendantsByType(db, context, 'OUT-001', 'ShippingOrder')
// Returns: ['SHIP-001']

updateParentChildRecord

Update child record in parent's childOrders array.

Function Signature:

typescript
async function updateParentChildRecord(
  parentReference: string,
  childReference: string,
  newStatus: string,
  db: Db,
  context: Context
): Promise<void>

Example Usage:

typescript
import { updateParentChildRecord } from '#lib/order/descendants'

// Child completed - update parent's record
await updateParentChildRecord(
  'OUT-001',      // Parent
  'PICK-001',     // Child
  'COMPLETED',    // New status
  db,
  context
)

// Parent's childOrders array updated:
// { ref: 'PICK-001', status: 'COMPLETED', ... }

Lifecycle Validation

Located in: #lib/order/helpers

canCompleteOrder

Check if order can be completed.

Function Signature:

typescript
async function canCompleteOrder(
  order: BaseOrder,
  orderType: string,
  database: any,
  context: Context
): Promise<OrderCompletionValidation>

Returns:

typescript
interface OrderCompletionValidation {
  allowed: boolean
  reason?: string
  blockers?: Array<{
    reference: string
    type: string
    currentStatus: string
    requiredStatuses?: string[]
  }>
}

Example Usage:

typescript
import { canCompleteOrder } from '#lib/order/helpers'

const validation = await canCompleteOrder(
  order,
  'OutboundOrder',
  db,
  context
)

if (!validation.allowed) {
  console.error(validation.reason)
  console.log('Blockers:', validation.blockers)
  // Cannot complete: 2 child orders not in required status
  // Blockers: [{ ref: 'PICK-001', status: 'IN_PROGRESS', ... }]
} else {
  // Safe to complete
  await db.updateOne({ reference }, { $set: { status: 'COMPLETED' } })
}

canCancelOrder

Check if order can be cancelled.

Function Signature:

typescript
async function canCancelOrder(
  order: BaseOrder,
  orderType: string,
  database: any,
  context: Context
): Promise<OrderCancellationValidation>

Returns:

typescript
interface OrderCancellationValidation {
  allowed: boolean
  reason?: string
  childAction?: 'CASCADE_CANCEL' | 'ORPHAN' | 'COMPLETE_FIRST' | 'SUSPEND'
  activeChildren?: Array<{
    reference: string
    type: string
    status: string
  }>
}

Example Usage:

typescript
import { canCancelOrder } from '#lib/order/helpers'

const validation = await canCancelOrder(
  order,
  'OutboundOrder',
  db,
  context
)

if (!validation.allowed) {
  console.error(validation.reason)
  console.log('Child action:', validation.childAction)
  // Cannot cancel: 2 active child orders in progress
  // Child action: CASCADE_CANCEL
} else {
  // Cancel order and execute child action
  await db.updateOne({ reference }, { $set: { status: 'CANCELLED' } })
  await cascadeCancellation(order, validation.childAction, db, context)
}

shouldAutoCompleteParent

Check if child completion should trigger parent auto-completion.

Function Signature:

typescript
async function shouldAutoCompleteParent(
  childOrder: BaseOrder,
  childOrderType: string,
  parentOrder: BaseOrder,
  parentOrderType: string,
  database: any,
  context: Context
): Promise<boolean>

Example Usage:

typescript
import { shouldAutoCompleteParent } from '#lib/order/helpers'

// Child just completed
await db.updateOne({ reference: childRef }, { $set: { status: 'COMPLETED' } })

// Check if parent should auto-complete
const shouldComplete = await shouldAutoCompleteParent(
  childOrder,
  'InternalOrder',
  parentOrder,
  'OutboundOrder',
  db,
  context
)

if (shouldComplete) {
  // All siblings complete → auto-complete parent
  await db.updateOne(
    { reference: parentOrder.reference },
    { $set: { status: 'COMPLETED' } }
  )
}

getLifecyclePolicy

Get lifecycle policy for an order type.

Function Signature:

typescript
function getLifecyclePolicy(orderType: string): OrderLifecyclePolicy | null

Example Usage:

typescript
import { getLifecyclePolicy } from '#lib/order/helpers'

const policy = getLifecyclePolicy('OutboundOrder')

if (policy) {
  console.log('Requires all children complete:', policy.parentCompletion.requireAllChildrenComplete)
  console.log('Child action on cancel:', policy.parentCancellation.childAction)
}

Process-Stage Validation

Located in: #lib/order/helpers

isValidStageForProcess

Check if a stage is valid for a process.

Function Signature:

typescript
function isValidStageForProcess(
  orderType: string,
  process: string,
  stage: string
): StageValidationResult

Returns:

typescript
interface StageValidationResult {
  valid: boolean
  reason?: string
  allowedStages?: string[]
  validNextStages?: string[]
}

Example Usage:

typescript
import { isValidStageForProcess } from '#lib/order/helpers'

const validation = isValidStageForProcess(
  'OutboundOrder',
  'FULFILLMENT',
  'PICKING'
)

if (!validation.valid) {
  console.error(validation.reason)
  console.log('Allowed stages:', validation.allowedStages)
} else {
  // Stage is valid for this process
}

isValidStageTransition

Check if a stage transition is allowed.

Function Signature:

typescript
function isValidStageTransition(
  orderType: string,
  process: string,
  fromStage: string,
  toStage: string
): StageValidationResult

Example Usage:

typescript
import { isValidStageTransition } from '#lib/order/helpers'

const validation = isValidStageTransition(
  'OutboundOrder',
  'FULFILLMENT',
  'PICKING',
  'PACKING'
)

if (!validation.valid) {
  console.error(validation.reason)
  console.log('Valid next stages:', validation.validNextStages)
  // Cannot transition from PICKING to SHIPPING
  // Valid next stages: ['PACKING']
} else {
  // Transition is valid
  await db.updateOne({ reference }, { $set: { 'flow.stage': 'PACKING' } })
}

getValidNextStages

Get allowed next stages from current stage.

Function Signature:

typescript
function getValidNextStages(
  orderType: string,
  process: string,
  currentStage: string
): string[]

Example Usage:

typescript
import { getValidNextStages } from '#lib/order/helpers'

const nextStages = getValidNextStages(
  'OutboundOrder',
  'FULFILLMENT',
  'PICKING'
)

console.log('Next stages:', nextStages)
// ['PACKING']

// Use in UI to show operators valid options

getAllowedStagesForProcess

Get all valid stages for a process.

Function Signature:

typescript
function getAllowedStagesForProcess(
  orderType: string,
  process: string
): string[]

Example Usage:

typescript
import { getAllowedStagesForProcess } from '#lib/order/helpers'

const stages = getAllowedStagesForProcess(
  'OutboundOrder',
  'FULFILLMENT'
)

console.log('Allowed stages:', stages)
// ['ALLOCATION', 'PICKING', 'PACKING', 'SHIPPING', 'DELIVERY']

isTerminalStage

Check if a stage is terminal (no outgoing transitions).

Function Signature:

typescript
function isTerminalStage(
  orderType: string,
  process: string,
  stage: string
): boolean

Example Usage:

typescript
import { isTerminalStage } from '#lib/order/helpers'

const isTerminal = isTerminalStage(
  'OutboundOrder',
  'FULFILLMENT',
  'DELIVERY'
)

if (isTerminal) {
  // No more stages → auto-complete order
  await db.updateOne({ reference }, { $set: { status: 'COMPLETED' } })
}

Compensation Functions

Located in: #lib/order/helpers

compensateOnParentFailure

Execute compensation when parent fails.

Function Signature:

typescript
async function compensateOnParentFailure(
  parentOrder: BaseOrder,
  orderType: string,
  database: any,
  context: Context
): Promise<CompensationResult>

Returns:

typescript
interface CompensationResult {
  success: boolean
  compensatedOrders: string[]
  failedCompensations: Array<{
    orderReference: string
    error: string
  }>
  strategy: CompensationAction
}

Example Usage:

typescript
import { compensateOnParentFailure } from '#lib/order/helpers'

// Parent failed - execute compensation
const compensation = await compensateOnParentFailure(
  order,
  'OutboundOrder',
  db,
  context
)

console.log('Strategy:', compensation.strategy)
console.log('Compensated orders:', compensation.compensatedOrders)
console.log('Failures:', compensation.failedCompensations)

if (!compensation.success) {
  // Some compensations failed - alert operators
  await alertOperators({ failures: compensation.failedCompensations })
}

compensateOnChildFailure

Execute compensation when child fails.

Function Signature:

typescript
async function compensateOnChildFailure(
  parentOrder: BaseOrder,
  failedChild: { reference: string; type: string },
  orderType: string,
  database: any,
  context: Context
): Promise<CompensationResult>

Example Usage:

typescript
import { compensateOnChildFailure } from '#lib/order/helpers'

// Child failed - compensate parent and siblings
const compensation = await compensateOnChildFailure(
  parentOrder,
  { reference: 'PICK-001', type: 'InternalOrder' },
  'OutboundOrder',
  db,
  context
)

console.log('Compensated:', compensation.compensatedOrders)
// ['PACK-001', 'SHIP-001'] - siblings cancelled

cascadeCancellation

Execute cascading cancellation.

Function Signature:

typescript
async function cascadeCancellation(
  parentOrder: BaseOrder,
  childAction: 'CASCADE_CANCEL' | 'ORPHAN' | 'COMPLETE_FIRST' | 'SUSPEND',
  database: any,
  context: Context
): Promise<{ updated: number; errors: any[] }>

Example Usage:

typescript
import { cascadeCancellation } from '#lib/order/helpers'

// Cancel order with cascade
const result = await cascadeCancellation(
  order,
  'CASCADE_CANCEL',
  db,
  context
)

console.log('Children updated:', result.updated)
console.log('Errors:', result.errors)

getCompensationStrategy

Get compensation strategy for an order type.

Function Signature:

typescript
function getCompensationStrategy(orderType: string): CompensationStrategy | null

Example Usage:

typescript
import { getCompensationStrategy } from '#lib/order/helpers'

const strategy = getCompensationStrategy('OutboundOrder')

if (strategy) {
  console.log('On child failure:', strategy.onChildFailure.action)
  console.log('On parent failure:', strategy.onParentFailure.action)
}

Usage Patterns

Manual API Route

typescript
import {
  canCompleteOrder,
  isValidStageTransition,
  updateParentChildRecord
} from '#lib/order/helpers'

// Complete order route
app.patch('/:reference/complete', async (req, rep) => {
  const order = await db.findOne({ reference: req.params.reference })
  
  // Validate completion
  const validation = await canCompleteOrder(order, 'OutboundOrder', db, context)
  
  if (!validation.allowed) {
    return rep.status(400).send({
      error: true,
      reason: validation.reason,
      blockers: validation.blockers
    })
  }
  
  // Complete order
  await db.updateOne({ reference }, { $set: { status: 'COMPLETED' } })
  
  // Notify parent
  if (order.hierarchy?.parentOrderReference) {
    await updateParentChildRecord(
      order.hierarchy.parentOrderReference,
      reference,
      'COMPLETED',
      db,
      context
    )
  }
  
  return rep.send({ error: false, message: 'Order completed' })
})

Pipeline Stage Handler

typescript
import {
  isValidStageTransition,
  compensateOnParentFailure
} from '#lib/order/helpers'

// Pipeline transition handler
async function handleSuccessTransition(stage, execution) {
  if (stage.nextStage) {
    // Validate stage transition
    const validation = isValidStageTransition(
      execution.orderType,
      order.flow.process,
      order.flow.stage,
      stage.nextStage
    )
    
    if (!validation.valid) {
      // Invalid transition - fail pipeline
      stage.status = 'FAILED'
      stage.errors.push({ message: validation.reason })
      
      // Execute compensation
      const compensation = await compensateOnParentFailure(
        order,
        execution.orderType,
        db,
        context
      )
      
      return
    }
    
    // Valid - update stage
    await db.updateOne({ reference }, { $set: { 'flow.stage': stage.nextStage } })
  }
}

Custom Background Job

typescript
import {
  getAllDescendants,
  canCompleteOrder
} from '#lib/order/helpers'

// Auto-complete stale orders
async function autoCompleteStaleOrders() {
  const staleOrders = await db.find({
    status: 'IN_PROGRESS',
    'updated.at.timestamp': { $lt: Date.now() - 86400000 }  // 24h old
  }).toArray()
  
  for (const order of staleOrders) {
    // Check if can complete
    const validation = await canCompleteOrder(order, order.orderType, db, context)
    
    if (validation.allowed) {
      // Complete order
      await db.updateOne(
        { reference: order.reference },
        { $set: { status: 'COMPLETED', completedBy: 'AUTO_COMPLETE_JOB' } }
      )
      
      console.log(`Auto-completed ${order.reference}`)
    }
  }
}

Best Practices

Always Validate

✅ Validate before operations:

typescript
const validation = await canCompleteOrder(order, type, db, context)
if (!validation.allowed) {
  return { error: true, reason: validation.reason }
}
// Proceed with operation

❌ Don't skip validation:

typescript
// Dangerous - no validation
await db.updateOne({ ref }, { status: 'COMPLETED' })

Handle Errors

✅ Check for errors:

typescript
const result = await createChildOrder(params, db, context)
if (result.error) {
  console.error(result.error)
  return { error: true, message: result.error }
}

Use Appropriate Functions

✅ Use specific functions:

typescript
// For descendants
const descendants = await getAllDescendants(db, context, ref)

// For validation
const validation = await canCompleteOrder(order, type, db, context)

// For compensation
const result = await compensateOnParentFailure(order, type, db, context)

Compose Operations

✅ Build complex flows:

typescript
// 1. Create child
const child = await createChildOrder(params, db, context)

// 2. Validate stage transition
const validation = isValidStageTransition(type, process, from, to)

// 3. Check completion eligibility
const canComplete = await canCompleteOrder(order, type, db, context)

// 4. Execute compensation if needed
const compensation = await compensateOnParentFailure(order, type, db, context)

Next Steps


Related Documentation: