Skip to content

Workflows - Manual vs Automated

Understanding the dual operation modes of the order system - traditional manual operations and optional pipeline automation using the same validation layer.

Dual Operation Modes

Flexible order management with two approaches:

  • Manual Operations - Direct API calls for full control
  • Pipeline Automation - Workflow orchestration for scale
  • Shared Validation - Same business rules for both modes
  • Gradual Adoption - Start manual, add automation later
  • Hybrid Workflows - Mix manual and automated as needed
  • No Lock-in - Switch between modes freely

Philosophy: Traditional system first, optional automation layer on top.

Core Philosophy

The De. order system is built on a fundamental principle: it works perfectly as a traditional manual system, with pipeline automation as an optional enhancement.

Traditional System First

typescript
// ✅ Manual operations work standalone
POST   /api/lsp/orders/tasks/create       // Create task
PATCH  /api/lsp/orders/:ref/stage         // Update stage
PATCH  /api/lsp/orders/:ref/complete      // Complete with validation

No pipeline required:

  • Create orders via API
  • Update stages manually
  • Complete when ready
  • All validations enforced
  • All business rules applied

Optional Automation Layer

typescript
// ✅ Pipeline enhances but doesn't replace
Pipeline templates automate:
- Child order creation
- Stage transitions
- Failure handling
- Workflow orchestration

Pipeline sits on top:

  • Uses same helper functions
  • Same validation layer
  • Same business rules
  • Enhances, doesn't replace

Why This Matters

Gradual Adoption:

Week 1: Manual operations
  ↓ Learn the system
Week 4: Add simple automation (auto-create children)
  ↓ Prove value
Week 8: Add full pipeline automation
  ↓ Scale operations

No Vendor Lock-in:

  • System works without pipelines
  • Can switch back to manual anytime
  • Mix manual + automated
  • Choose per operation

Manual Operations

Creating Orders

Inbound Order (External Shipment)

bash
POST /api/lsp/orders/inbound/create
Content-Type: application/json

{
  "vendor": "VENDOR-123",
  "expectedDate": "2024-03-15",
  "items": [
    { "sku": "ITEM-001", "quantity": 100 },
    { "sku": "ITEM-002", "quantity": 50 }
  ],
  "flow": {
    "process": "RECEIVING",
    "stage": "ARRIVAL",
    "status": "PENDING"
  }
}

// Response:
{
  "error": false,
  "reference": "IN-12345",
  "message": "Inbound order created"
}

Internal Task (Warehouse Operation)

bash
POST /api/lsp/orders/tasks/create
Content-Type: application/json

{
  "parentOrderReference": "OUT-001",
  "process": "PICKING",
  "items": [
    { "sku": "ITEM-001", "location": "A-12-3", "quantity": 2 }
  ],
  "assignee": {
    "operator": "OPR-123"
  }
}

// Response:
{
  "error": false,
  "reference": "PICK-001",
  "message": "Picking task created"
}

Shipping Order (Last-Mile)

bash
POST /api/lsp/orders/shipping/create
Content-Type: application/json

{
  "parentOrderReference": "OUT-001",
  "carrier": "CARRIER-001",
  "driver": "DRV-456",
  "destination": {
    "address": "123 Main St",
    "city": "Boston",
    "zipCode": "02101"
  },
  "timeSlot": {
    "from": "2024-03-15T14:00:00Z",
    "to": "2024-03-15T16:00:00Z"
  }
}

// Response:
{
  "error": false,
  "reference": "SHIP-001",
  "message": "Shipping order created"
}

Updating Stages

Manual Stage Transition

bash
PATCH /api/lsp/orders/PICK-001/stage
Content-Type: application/json

{
  "orderType": "InternalOrder",
  "newStage": "PICKING"
}

// With validation:
// - Checks current stage (ASSIGNED)
// - Validates transition (ASSIGNED  PICKING) ✅
// - Updates if valid

// Response:
{
  "error": false,
  "status": "ORDER::STAGE_UPDATED",
  "previousStage": "ASSIGNED",
  "newStage": "PICKING"
}

Invalid Transition Prevented

bash
PATCH /api/lsp/orders/PICK-001/stage
Content-Type: application/json

{
  "orderType": "InternalOrder",
  "newStage": "VERIFICATION"  // Skipping PICKING stage
}

// Validation fails:
{
  "error": true,
  "status": "ORDER::INVALID_STAGE_TRANSITION",
  "message": "Cannot transition from ASSIGNED to VERIFICATION",
  "currentStage": "ASSIGNED",
  "requestedStage": "VERIFICATION",
  "validNextStages": ["TRAVELING"]  // Only valid option
}

Completing Orders

Complete with Validation

bash
PATCH /api/lsp/orders/OUT-001/complete
Content-Type: application/json

{
  "orderType": "OutboundOrder"
}

// Validation checks:
// 1. Are all children complete?
// 2. Are mandatory children done?
// 3. Do any children block completion?

// If all children complete:
{
  "error": false,
  "status": "ORDER::COMPLETED",
  "message": "Order completed successfully"
}

// If children not ready:
{
  "error": true,
  "status": "ORDER::COMPLETION_BLOCKED",
  "message": "Cannot complete: 2 child orders not in required status",
  "blockers": [
    {
      "reference": "PICK-001",
      "type": "InternalOrder",
      "currentStatus": "IN_PROGRESS",
      "requiredStatuses": ["COMPLETED"]
    },
    {
      "reference": "PACK-001",
      "type": "InternalOrder",
      "currentStatus": "PENDING",
      "requiredStatuses": ["COMPLETED"]
    }
  ]
}

Cancelling Orders

Cancel with Compensation

bash
PATCH /api/lsp/orders/OUT-001/cancel
Content-Type: application/json

{
  "orderType": "OutboundOrder",
  "reason": "Customer requested cancellation"
}

// Validation checks:
// 1. Can cancel with active children?
// 2. What should happen to children?

// If allowed:
{
  "error": false,
  "status": "ORDER::CANCELLED",
  "message": "Order cancelled successfully",
  "compensation": {
    "strategy": "CASCADE_CANCEL",
    "childrenUpdated": 3,
    "errors": []
  }
}

Handling Failures

Mark Failed with Compensation

bash
PATCH /api/lsp/orders/PICK-001/fail
Content-Type: application/json

{
  "orderType": "InternalOrder",
  "reason": "Items not found in location",
  "failureCode": "INVENTORY_MISMATCH"
}

// Executes compensation:
// 1. Mark order failed
// 2. Trigger parent compensation
// 3. Execute cleanup actions

// Response:
{
  "error": false,
  "status": "ORDER::FAILED",
  "message": "Order marked as failed",
  "compensation": {
    "self": {
      "strategy": "MARK_FAILED",
      "compensatedOrders": ["PICK-001"]
    },
    "parent": {
      "strategy": "ROLLBACK_SIBLINGS",
      "compensatedOrders": ["PACK-001", "SHIP-001"],
      "failures": []
    }
  }
}

Manual Workflow Example

E-Commerce Fulfillment (All Manual)

typescript
// 1. Create outbound order (customer order)
const outbound = await POST('/api/lsp/orders/outbound/create', {
  customer: 'CUST-123',
  items: [...]
})
// OUT-001 created

// 2. Manually create picking task
const picking = await POST('/api/lsp/orders/tasks/create', {
  parentOrderReference: 'OUT-001',
  process: 'PICKING',
  items: [...]
})
// PICK-001 created

// 3. Operator picks items
await PATCH('/api/lsp/orders/PICK-001/stage', { newStage: 'PICKING' })
// ... picking happens ...
await PATCH('/api/lsp/orders/PICK-001/complete', { orderType: 'InternalOrder' })

// 4. Manually create packing task
const packing = await POST('/api/lsp/orders/tasks/create', {
  parentOrderReference: 'OUT-001',
  process: 'PACKING',
  items: [...]
})
// PACK-001 created

// 5. Operator packs items
await PATCH('/api/lsp/orders/PACK-001/stage', { newStage: 'PACKING' })
// ... packing happens ...
await PATCH('/api/lsp/orders/PACK-001/complete', { orderType: 'InternalOrder' })

// 6. Manually create shipping order
const shipping = await POST('/api/lsp/orders/shipping/create', {
  parentOrderReference: 'OUT-001',
  carrier: 'CARRIER-001',
  destination: {...}
})
// SHIP-001 created

// 7. Driver delivers
await PATCH('/api/lsp/orders/SHIP-001/stage', { newStage: 'DELIVERED' })
await PATCH('/api/lsp/orders/SHIP-001/complete', { orderType: 'ShippingOrder' })

// 8. Complete parent order (with validation)
await PATCH('/api/lsp/orders/OUT-001/complete', { orderType: 'OutboundOrder' })
// ✅ All children complete → parent completes

All operations:

  • ✅ Manual API calls
  • ✅ Full validation at each step
  • ✅ Lifecycle policies enforced
  • ✅ Process-stage validation
  • ✅ No automation required

Pipeline Automation

How Pipelines Work

Pipelines use the same helper functions as manual operations:

typescript
// Manual route
.patch('/:ref/complete', async (req, rep) => {
  const validation = await canCompleteOrder(order, type, db, context)
  // ✅ Uses helper function
})

// Pipeline automation
async function handleSuccessTransition() {
  const validation = await canCompleteOrder(order, type, db, context)
  // ✅ Same helper function
}

Result: Consistent validation whether manual or automated.

Pipeline Template Structure

typescript
{
  id: 'pipe-outbound-v1',
  name: 'Standard Outbound Fulfillment',
  orderType: 'OutboundOrder',
  
  stages: [
    {
      name: 'Inventory Allocation',
      nextStage: 'ALLOCATION',
      onSuccess: {
        action: 'CREATE_CHILD',
        childOrderType: 'InternalOrder',
        creationReason: 'PIPELINE_STAGE',
        relationship: {
          type: 'MANDATORY',
          canBlockParent: true
        }
      }
    },
    {
      name: 'Picking',
      nextStage: 'PICKING',
      onSuccess: {
        action: 'CONTINUE'
      }
    },
    {
      name: 'Packing',
      nextStage: 'PACKING',
      onSuccess: {
        action: 'CREATE_CHILD',
        childOrderType: 'InternalOrder'
      }
    },
    {
      name: 'Shipping',
      nextStage: 'SHIPPING',
      onSuccess: {
        action: 'CREATE_CHILD',
        childOrderType: 'ShippingOrder'
      },
      onFailure: {
        action: 'HALT',
        compensation: true
      }
    }
  ]
}

Automated Workflow Example

E-Commerce Fulfillment (Pipeline Automated)

typescript
// 1. Create outbound order with pipeline
const outbound = await POST('/api/lsp/orders/outbound/create', {
  customer: 'CUST-123',
  items: [...],
  pipelineId: 'pipe-outbound-v1'  // ← Attach pipeline
})

// Pipeline automatically:
// ↓
// Stage 1: Inventory Allocation
// - Validates stage transition ✅
// - Allocates inventory
// - Auto-creates PICK-001 (InternalOrder)
// - Composition metadata added
// ↓
// Stage 2: Picking
// - PICK-001 assigned to operator
// - Operator scans items
// - Auto-transitions to PICKING stage
// - Auto-completes when done
// ↓
// Stage 3: Packing
// - Auto-creates PACK-001 (InternalOrder)
// - Operator packs
// - Auto-completes when done
// ↓
// Stage 4: Shipping
// - Auto-creates SHIP-001 (ShippingOrder)
// - Driver assigned
// - Delivery tracked
// - Auto-completes on delivery
// ↓
// Parent Auto-Completion
// - All children complete
// - Validates parent completion ✅
// - Auto-completes OUT-001

All automated, but:

  • ✅ Same validation as manual
  • ✅ Same lifecycle policies
  • ✅ Same process-stage rules
  • ✅ Same compensation strategies

Child Creation with Pipelines

typescript
// Pipeline creates child using same function
await createChildOrder({
  parentOrder,
  childOrderType: 'InternalOrder',
  childOrderId: generateId(),
  childOrderReference: `PICK-${generateCode()}`,
  lspOrderType: 'InternalOrder',
  processor: context,
  
  // Composition metadata (tracked)
  creationReason: 'PIPELINE_STAGE',
  created: {
    by: {
      type: 'PIPELINE',
      id: 'pipe-outbound-v1',
      name: 'Standard Outbound Pipeline'
    },
    at: { timestamp: Date.now(), timezone: 'UTC' }
  },
  relationship: {
    type: 'MANDATORY',
    canBlockParent: true,
    sequence: 1
  },
  metadata: {
    trigger: 'allocation_complete',
    notes: 'Auto-created picking task'
  }
}, db, context)

// ✅ Same function as manual creation
// ✅ Same validation
// ✅ Same descendant tracking

Stage Transitions with Pipelines

typescript
// Pipeline transitions stage
const validation = isValidStageTransition(
  orderType,
  order.flow.process,
  order.flow.stage,
  nextStage
)

if (!validation.valid) {
  // ❌ Invalid transition - fail pipeline
  stage.status = 'FAILED'
  stage.errors.push({
    code: 'INVALID_STAGE_TRANSITION',
    message: validation.reason
  })
  return
}

// ✅ Valid - update stage
await db.updateOne(
  { reference: order.reference },
  { $set: { 'flow.stage': nextStage } }
)

// Same validation as manual operations

Compensation with Pipelines

typescript
// Pipeline fails - trigger compensation
case 'HALT':
  execution.status = 'HALTED'
  
  if (execution.orderId) {
    const order = await fetchOrder(orderId)
    
    // Execute compensation (same as manual)
    const compensation = await compensateOnParentFailure(
      order,
      orderType,
      db,
      context
    )
    
    // Log results
    stage.metadata.compensation = compensation
  }
  break

Hybrid Workflows

Mix Manual and Automated

Start automated, intervene manually when needed:

typescript
// 1. Pipeline creates and processes order
const outbound = await POST('/api/lsp/orders/outbound/create', {
  pipelineId: 'pipe-outbound-v1'
})

// Pipeline automatically:
// - Creates PICK-001
// - Creates PACK-001
// - Creates SHIP-001

// 2. Exception occurs - package damaged
// Operator manually creates exception task
const exception = await POST('/api/lsp/orders/tasks/create', {
  parentOrderReference: 'OUT-001',
  process: 'EXCEPTION',
  creationReason: 'MANUAL_REQUEST',  // Manual, not pipeline
  relationship: {
    type: 'OPTIONAL',
    canBlockParent: false  // Don't block shipment
  },
  notes: 'Package damaged during packing - replace box'
})

// 3. Pipeline continues normally
// Exception task runs in parallel
// Doesn't block parent completion

// ✅ Hybrid: Automated + Manual intervention

Manual Override of Automation

Pipeline automation with manual control points:

typescript
// Pipeline template with manual approval
{
  stages: [
    {
      name: 'Picking',
      nextStage: 'PICKING',
      onSuccess: {
        action: 'WAIT_FOR_APPROVAL',  // Manual checkpoint
        approverRole: 'SUPERVISOR'
      }
    },
    {
      name: 'Packing',
      nextStage: 'PACKING',
      onSuccess: {
        action: 'CONTINUE'  // Auto-proceed
      }
    }
  ]
}

// Workflow:
// 1. Auto-create picking task ✅
// 2. Operator picks items ✅
// 3. Wait for supervisor approval ⏸️ (Manual)
// 4. Supervisor approves → continue
// 5. Auto-create packing task ✅
// 6. Auto-complete ✅

Comparison: Manual vs Automated

AspectManual OperationsPipeline Automation
ControlFull manual controlAutomated workflow
SpeedSlower (human steps)Faster (automated)
ScalabilityLimited by operatorsHigh scalability
FlexibilityVery flexibleTemplate-constrained
Validation✅ Same✅ Same
Business Rules✅ Same✅ Same
Error HandlingManual interventionAuto-compensation
Learning CurveSimple API callsTemplate design needed
Best ForSmall operations, exceptionsHigh volume, standard workflows

When to Use Each Mode

Use Manual Operations When:

Low Volume

  • < 100 orders per day
  • Simple operations
  • API calls sufficient

High Variability

  • Every order different
  • Custom workflows
  • Exception handling

Learning Phase

  • New to the system
  • Understanding flows
  • Testing scenarios

Exceptions

  • One-off situations
  • Edge cases
  • Manual judgment needed

Use Pipeline Automation When:

High Volume

  • 1,000+ orders per day
  • Repetitive workflows
  • Consistency needed

Standard Processes

  • Well-defined workflows
  • Predictable steps
  • Few exceptions

Scale Requirements

  • Growing operations
  • Need automation
  • Resource optimization

Complex Hierarchies

  • Multi-level workflows
  • Many child orders
  • Automatic coordination

Use Hybrid Approach When:

Most Real Operations

  • Standard flow automated
  • Exceptions manual
  • Best of both worlds

Gradual Automation

  • Start manual
  • Automate common paths
  • Keep manual for edge cases

Operator-Assisted

  • Automation with checkpoints
  • Manual approvals
  • Human-in-the-loop

Migration Path

Start Manual

Week 1-4: Learn the system

typescript
// All manual operations
POST   /api/lsp/orders/tasks/create
PATCH  /api/lsp/orders/:ref/stage
PATCH  /api/lsp/orders/:ref/complete

Benefits:

  • Understand order flows
  • Learn validation rules
  • See how hierarchy works
  • No automation complexity

Add Simple Automation

Week 5-8: Automate child creation

typescript
// Pipeline just creates children
// Everything else manual
{
  stages: [
    {
      name: 'Start',
      onSuccess: {
        action: 'CREATE_CHILD',  // Auto-create
        childOrderType: 'InternalOrder'
      }
    }
  ]
}

Benefits:

  • Reduce manual child creation
  • Maintain manual control
  • Gradual complexity

Full Automation

Week 9+: Complete workflows

typescript
// Full pipeline automation
{
  stages: [
    { name: 'Allocation', onSuccess: { action: 'CREATE_CHILD' } },
    { name: 'Picking', onSuccess: { action: 'CONTINUE' } },
    { name: 'Packing', onSuccess: { action: 'CREATE_CHILD' } },
    { name: 'Shipping', onSuccess: { action: 'COMPLETE_PIPELINE' } }
  ]
}

Benefits:

  • Full automation
  • Maximum efficiency
  • Proven workflows
  • Scale operations

Best Practices

Start Simple

✅ Begin with manual:

typescript
// Week 1: All manual
createOrder()
updateStage()
completeOrder()

✅ Add automation gradually:

typescript
// Week 4: Simple pipeline
{ action: 'CREATE_CHILD' }

// Week 8: Full pipeline
{ action: 'COMPLETE_PIPELINE' }

Keep Manual Option

✅ Always allow manual override:

typescript
// Even with pipelines, support:
POST   /api/lsp/orders/tasks/create  // Manual creation
PATCH  /api/lsp/orders/:ref/stage    // Manual stage update
PATCH  /api/lsp/orders/:ref/cancel   // Manual cancellation

Monitor Both Modes

✅ Track automation vs manual:

typescript
// Composition metadata tells you
childOrders: [{
  creationReason: 'PIPELINE_STAGE',  // Automated
  created: { by: { type: 'PIPELINE' } }
}]

vs

childOrders: [{
  creationReason: 'MANUAL_REQUEST',  // Manual
  created: { by: { type: 'USER', id: 'OPR-123' } }
}]

Design for Flexibility

✅ Don't force automation:

typescript
// Support both:
if (req.body.pipelineId) {
  // Use pipeline
} else {
  // Manual workflow
}

Use Cases

Small 3PL (Manual)

Operation: 50 orders/day, high variability

Approach:

  • All manual API calls
  • Custom workflow per client
  • No automation needed
  • Full flexibility

Why: Volume doesn't justify automation complexity

Large Fulfillment Center (Automated)

Operation: 10,000 orders/day, standard flow

Approach:

  • Pipeline automation
  • Standard workflows
  • Exception handling manual
  • Scale with automation

Why: Volume requires automation, standard processes

Mid-Size Warehouse (Hybrid)

Operation: 500 orders/day, mostly standard

Approach:

  • Pipelines for standard orders (90%)
  • Manual for exceptions (10%)
  • Best of both worlds

Why: Mix of volume + variability

Next Steps


Related Documentation: