Unified Order Service
Home
Getting Started
  • Core API
  • Drone API
Resources
Home
Getting Started
  • Core API
  • Drone API
Resources
  • Introduction

    • Getting Started
    • System Overview
    • Authentication
  • Core Concepts

    • Working with Orders
    • Working with Channels
    • Places
    • Storage Locations
    • Places Management Dashboard
  • Integration

    • Webhooks
    • Order Status Workflows
    • FCM Push Notifications
  • Advanced Topics

    • API Keys
    • Order Versioning
    • Picking App Integration Guide
    • Subscriptions
    • Cancellation Reasons

Picking App Integration Guide

This guide explains how to integrate picking applications with the Unified Order Service, including important scope restrictions and architectural considerations.

Overview

The picking app is a specialized component in the order fulfillment ecosystem with limited scope to maintain system integrity and prevent conflicts between different operational domains.

System Architecture

The UOS employs a separation of concerns architecture where different systems handle different parts of the order lifecycle:

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│ Order Management│    │   Picking App   │    │ Logistics/Ship  │    │ Delivery/Collection│
│    Systems      │    │                 │    │    Systems      │    │    Systems      │
├─────────────────┤    ├─────────────────┤    ├─────────────────┤    ├─────────────────┤
│ • pending       │───▶│ • picking       │───▶│ • shipped       │───▶│ • completed     │
│ • processing    │    │ • picked        │    │ • out_for_del.  │    │ • collected     │
│ • cancelled*    │    │ • cancelled*    │    │                 │    │ • delivered     │
└─────────────────┘    └─────────────────┘    └─────────────────┘    └─────────────────┘

*Cancellation can be initiated from any domain but affects the entire order.

Scope Restrictions for Picking App

What the Picking App CAN Control

The picking app has full control over these order statuses:

StatusCodePurposeWhen to Use
picking2Start/continue pickingWhen beginning item collection
picked3Complete pickingWhen all items are collected
cancelled6Cancel during pickingWhen order cannot be fulfilled

What the Picking App CANNOT Control

These statuses are managed by other systems:

StatusCodeManaged ByWhy Restricted
pending1Order managementInitial order state and business logic
shipped4Logistics systemsShipping/carrier integration
completed5Delivery systemsFinal delivery confirmation

Status Transition Rules

Valid Picking App Transitions

graph TD
    A[pending] -->|start picking| B[picking]
    A -->|cancel| F[cancelled]
    C[processing] -->|start picking| B
    C -->|cancel| F
    B -->|complete picking| D[picked]
    B -->|cancel| F
    D -->|cancel| F
    
    style A fill:#e1f5fe
    style B fill:#c8e6c9
    style D fill:#c8e6c9
    style F fill:#ffcdd2

Forbidden Transitions (Will Return HTTP 422)

  • Not Allowed: picked → shipped (logistics systems handle this)
  • Not Allowed: picking → completed (must go through proper workflow)
  • Not Allowed: Any transition FROM shipped or completed (terminal for picking app)

Implementation Guide

1. Error Handling

Always handle scope restriction errors:

async function updateOrderStatus(orderId, statusCode) {
  try {
    const response = await fetch(`/dts/drone/order/set_status?order_id=${orderId}&status=${statusCode}`);
    
    if (response.status === 422) {
      const error = await response.json();
      
      if (error.message.data.error_code === 'PICKING_APP_TRANSITION_NOT_ALLOWED') {
        // Show user the allowed transitions
        showAllowedTransitions(error.message.data.allowed_transitions);
        return;
      }
    }
    
    // Handle other errors...
  } catch (error) {
    console.error('Status update failed:', error);
  }
}

2. Status Validation

Validate transitions client-side before API calls:

const PICKING_APP_TRANSITIONS = {
  'pending': ['picking', 'cancelled'],
  'processing': ['picking', 'cancelled'],
  'picking': ['picked', 'cancelled'],
  'picked': ['cancelled']
};

function canTransitionTo(currentStatus, targetStatus) {
  const allowedTargets = PICKING_APP_TRANSITIONS[currentStatus];
  return allowedTargets && allowedTargets.includes(targetStatus);
}

// Usage
if (!canTransitionTo(order.status, 'shipped')) {
  alert('Cannot set status to shipped - this is handled by logistics systems');
  return;
}

3. Workflow Implementation

Follow the proper picking workflow:

class PickingWorkflow {
  async startPicking(orderId) {
    // 1. Check current status
    const order = await this.getOrder(orderId);
    
    if (!['pending', 'processing'].includes(order.status)) {
      throw new Error(`Cannot start picking from status: ${order.status}`);
    }
    
    // 2. Start picking
    return await this.updateStatus(orderId, 2); // picking
  }
  
  async completePicking(orderId) {
    // 1. Verify all items are picked
    const items = await this.getOrderItems(orderId);
    const allPicked = items.every(item => 
      ['picked', 'substituted', 'not_found'].includes(item.status)
    );
    
    if (!allPicked) {
      throw new Error('Cannot complete picking - not all items processed');
    }
    
    // 2. Complete picking
    return await this.updateStatus(orderId, 3); // picked
  }
}

📱 User Interface Guidelines

Status Display

Show status with appropriate context:

function formatOrderStatus(order) {
  const statusLabels = {
    'pending': { label: 'Ready to Pick', color: 'blue', canPick: true },
    'processing': { label: 'Ready to Pick', color: 'blue', canPick: true },
    'picking': { label: 'Being Picked', color: 'orange', canPick: false },
    'picked': { label: 'Picked - Awaiting Logistics', color: 'green', canPick: false },
    'shipped': { label: 'Shipped', color: 'purple', canPick: false },
    'completed': { label: 'Completed', color: 'gray', canPick: false }
  };
  
  return statusLabels[order.status] || { label: order.status, color: 'gray' };
}

Action Buttons

Only show available actions:

function getAvailableActions(orderStatus) {
  const actions = {
    'pending': ['Start Picking', 'Cancel'],
    'processing': ['Start Picking', 'Cancel'],
    'picking': ['Mark as Picked', 'Cancel'],
    'picked': ['Cancel'], // Limited options
    'shipped': [], // No actions available
    'completed': [] // No actions available
  };
  
  return actions[orderStatus] || [];
}

🔗 Integration with Other Systems

After Picking Complete

Once an order is picked, other systems take over:

  1. Logistics Systems will transition to shipped
  2. Delivery Systems will handle final delivery
  3. Picking App should monitor but not interfere

Monitoring Order Progress

Use read-only endpoints to track progress:

// Monitor order status without changing it
async function monitorOrder(orderId) {
  const order = await fetch(`/dts/drone/order/info?orderId=${orderId}`);
  
  // Can read any status, but only modify within scope
  console.log(`Order ${orderId} is now: ${order.status}`);
  
  if (['shipped', 'completed'].includes(order.status)) {
    // Update UI to show order is out of picking scope
    this.showOrderCompletedMessage();
  }
}

Common Pitfalls

1. Attempting to Set Shipping Status

// INCORRECT - DON'T DO THIS
await updateOrderStatus(orderId, 4); // Will fail with 422

// CORRECT - DO THIS INSTEAD
await updateOrderStatus(orderId, 3); // picked
// Let logistics systems handle shipping

2. Skipping Picking State

// INCORRECT - DON'T DO THIS
await updateOrderStatus(orderId, 3); // pending → picked (invalid)

// CORRECT - DO THIS INSTEAD
await updateOrderStatus(orderId, 2); // pending → picking
// ... do actual picking work ...
await updateOrderStatus(orderId, 3); // picking → picked

3. Not Handling Scope Errors

// INCORRECT - DON'T DO THIS
try {
  await updateOrderStatus(orderId, status);
} catch (error) {
  alert('Failed to update status'); // Generic error
}

// CORRECT - DO THIS INSTEAD
try {
  await updateOrderStatus(orderId, status);
} catch (error) {
  if (error.code === 'PICKING_APP_TRANSITION_NOT_ALLOWED') {
    alert(`Cannot change to this status. Allowed: ${error.allowedTransitions.join(', ')}`);
  } else {
    alert('Failed to update status');
  }
}

Testing Your Integration

Test Cases for Scope Validation

  1. Valid Transitions: Test all allowed transitions work correctly
  2. Invalid Transitions: Verify proper 422 error handling
  3. Read-Only Status: Confirm you can read but not set restricted statuses
  4. Error Messages: Check error messages are user-friendly

Example Test

describe('Picking App Status Integration', () => {
  it('should allow picking app transitions', async () => {
    const order = await createTestOrder('pending');
    
    // Should allow
    await updateOrderStatus(order.id, 2); // picking
    await updateOrderStatus(order.id, 3); // picked
  });
  
  it('should reject invalid transitions', async () => {
    const order = await createTestOrder('picked');
    
    // Should reject with 422
    await expect(updateOrderStatus(order.id, 4)).rejects.toMatchObject({
      status: 422,
      error_code: 'PICKING_APP_TRANSITION_NOT_ALLOWED'
    });
  });
});

📚 Related Documentation

  • Drone API Reference - Complete API documentation
  • Order Status Workflows - Overall status flow
  • Set Status Endpoint - Detailed endpoint docs

🎯 Best Practices Summary

  1. Stay in Scope: Only manage picking, picked, and cancelled statuses
  2. Follow Workflow: Always go pending → picking → picked
  3. Handle Errors: Implement proper 422 error handling
  4. Validate Client-Side: Check transitions before API calls
  5. Monitor Progress: Use read-only endpoints to track order progress
  6. Clear UI: Show users what actions are available for each status
Last Updated: 12/1/25, 11:31 AM
Prev
Order Versioning
Next
Subscriptions