Distributed Order Management: Orders, Inventory, and Fulfillment Quickstart

This guide focuses on implementing the core components of a Distributed Order Management (DOM) system using the Stateset API, with emphasis on Order Management, Inventory Control, and Fulfillment Orchestration across multiple locations and sales channels.

Orders Management Overview

Effective order management is the backbone of any successful eCommerce operation. It encompasses the entire lifecycle of an order, from the moment a customer places it to its successful delivery and potential after-sales service. A robust order management system is crucial for maintaining customer satisfaction, optimizing operational efficiency, and driving business growth.

The Importance of Efficient Orders Management

An efficient orders management system is essential for several reasons:

  1. Customer Satisfaction: Accurate and timely order processing directly impacts customer experience and loyalty.
  2. Operational Efficiency: Streamlined order processes reduce errors and save time and resources.
  3. Inventory Accuracy: Proper order management helps maintain accurate inventory levels across all sales channels.
  4. Data-Driven Insights: Order data provides valuable insights into sales trends, customer behavior, and operational performance.
  5. Scalability: An effective system allows businesses to handle increasing order volumes as they grow.

Challenges in Orders Management

Despite its importance, orders management can present several challenges:

  1. Multi-Channel Complexity: Managing orders across various sales channels (e-commerce, marketplaces, physical stores) can be complex.
  2. Inventory Synchronization: Keeping inventory levels accurate and synchronized across all channels is challenging.
  3. Order Routing: Determining the optimal fulfillment location for each order can be complex, especially for businesses with multiple warehouses.
  4. Order Modifications: Handling order changes or cancellations efficiently can be difficult.
  5. Integration Issues: Connecting order management with other systems (ERP, WMS) can be technically challenging.
  6. Peak Season Management: Handling increased order volumes during peak seasons without compromising efficiency.

StateSet’s Orders Management Solution

StateSet offers a comprehensive, automated orders management solution designed to address these challenges:

Key Features

  1. Centralized Order Management: A single platform to manage orders from all sales channels including eMail, Shopify, Amazon, TikTok Shop and more.
  2. Intelligent Order Routing: Automatically determines the best fulfillment location based on inventory levels, shipping costs, and delivery times.
  3. Real-Time Inventory Sync: Keeps inventory levels updated across all channels in real-time.
  4. Automated Order Processing: Streamlines order processing with customizable workflows and automation rules.
  5. Order Modification Handling: Easily manage order changes, cancellations, and returns.
  6. Integration Capabilities: Seamlessly connects with other business systems (ERP, WMS, CRM) for a unified operational view.
  7. Scalable Architecture: Designed to handle high order volumes, especially during peak seasons.
  8. Advanced Analytics: Provides detailed insights into order trends, fulfillment performance, and customer behavior.

Benefits

  1. Improved Customer Experience: Faster order processing and accurate delivery estimates enhance customer satisfaction.
  2. Increased Operational Efficiency: Automation reduces manual errors and speeds up order processing times.
  3. Better Inventory Management: Real-time sync prevents overselling and optimizes stock levels.
  4. Enhanced Decision Making: Data-driven insights help in making informed business decisions.
  5. Reduced Costs: Efficient order routing and processing lead to cost savings in fulfillment and shipping.
  6. Scalability: Easily handle growing order volumes without compromising on efficiency.

Implementing Stateset’s Orders Management Solution

To leverage StateSet’s orders management capabilities:

  1. Integration: Connect StateSet with your existing eCommerce platforms, marketplaces, and other sales channels.
  2. Configuration: Set up your business rules, workflows, and automation preferences within the StateSet platform.
  3. Training: Familiarize your team with the new system and its features.
  4. Data Migration: Transfer historical order data to gain comprehensive insights from day one.
  5. Continuous Optimization: Regularly review order data and KPIs to refine your processes and strategies.

By implementing StateSet’s Orders Management solution, businesses can transform their order processing from a potential bottleneck into a streamlined, efficient operation. This not only improves customer satisfaction but also drives operational excellence, supports scalable growth, and enables data-driven decision-making across the organization.

Table of Contents

  1. Setup
  2. Order Management
  3. Inventory Management
  4. Fulfillment Orchestration
  5. Cross-Channel Synchronization
  6. Reporting and Analytics

1. Setup

First, install the Stateset Node.js SDK:

npm install stateset-node

Initialize the Stateset client:

import { stateset } from 'stateset-node';

const client = stateset('YOUR_API_KEY');

2. Order Management

2.1 Unified Order Capture

Implement a unified order capture function that handles orders from multiple channels:

async function captureOrder(orderData) {
  try {
    let order;
    switch(orderData.type) {
      case 'standard':
        order = await client.order.create({
          source: orderData.channel,
          channel_order_id: orderData.channelOrderId,
          customer: orderData.customer,
          line_items: orderData.lineItems,
          total_price: orderData.totalPrice,
          currency: orderData.currency,
          shipping_address: orderData.shippingAddress,
          status: 'received'
        });
        break;
      case 'pre-order':
        order = await createPreOrder(orderData);
        break;
      case 'backorder':
        order = await createBackOrder(orderData);
        break;
      case 'subscription':
        order = await createSubscriptionOrder(orderData);
        break;
      default:
        throw new Error('Unsupported order type');
    }
    return order;
  } catch (error) {
    console.error("Error capturing order:", error);
    throw error;
  }
}

2.2 Intelligent Distributed Order Routing

Determine the optimal fulfillment location based on inventory availability and other factors:

async function routeOrder(orderId) {
  try {
    const order = await client.order.retrieve(orderId);
    const fulfillmentLocations = await client.location.list({ type: 'fulfillment' });

    if (!order || !fulfillmentLocations.length) {
      throw new Error('Invalid order or no fulfillment locations available');
    }

    const historicalData = await getHistoricalFulfillmentData(order.customer.id);
    const mlPrediction = await mlModel.predict({
      order: order,
      historicalData: historicalData
    });

    const inventoryLevels = await getInventoryLevels(order.line_items.map(item => item.product_id));
    const shippingRates = await getShippingRates(order);

    const optimalLocation = determineOptimalLocation(order, fulfillmentLocations, mlPrediction, shippingRates, inventoryLevels);

    if (!optimalLocation) {
      throw new Error('Unable to determine optimal fulfillment location');
    }

    const updatedOrder = await client.order.update(orderId, { 
      assigned_location: optimalLocation.id,
      status: 'routed',
      routing_metadata: {
        prediction_confidence: mlPrediction.confidence,
        selected_shipping_rate: optimalLocation.selectedRate
      }
    });

    await logRoutingDecision(orderId, optimalLocation, mlPrediction);

    return { 
      optimalLocation, 
      updatedOrder 
    };
  } catch (error) {
    console.error(`Error routing order ${orderId}:`, error);
    await client.order.update(orderId, { status: 'routing_failed' });
    throw error;
  }
}

function determineOptimalLocation(order, locations, mlPrediction, shippingRates, inventoryLevels) {
  const scoredLocations = locations.map(location => {
    const inventoryScore = calculateInventoryScore(location, order, inventoryLevels);
    const shippingScore = calculateShippingScore(location, shippingRates);
    const mlScore = mlPrediction.locationScores[location.id] || 0;
    const fulfillmentCost = estimateFulfillmentCost(location, order);

    const totalScore = (inventoryScore * 0.4) + (shippingScore * 0.3) + (mlScore * 0.2) - (fulfillmentCost * 0.1);

    return {
      ...location,
      score: totalScore,
      selectedRate: shippingRates[location.id].cheapestRate
    };
  });

  return scoredLocations.reduce((best, current) => 
    current.score > best.score ? current : best
  , scoredLocations[0]);
}

function calculateInventoryScore(location, order, inventoryLevels) {
  return order.line_items.reduce((score, item) => {
    const availability = inventoryLevels[location.id]?.[item.product_id] || 0;
    return score + (availability >= item.quantity ? 1 : 0);
  }, 0) / order.line_items.length;
}

function calculateShippingScore(location, shippingRates) {
  const locationRates = shippingRates[location.id];
  const cheapestRate = locationRates.cheapestRate;
  const fastestRate = locationRates.fastestRate;
  return 1 - (cheapestRate.price / fastestRate.price);
}

function estimateFulfillmentCost(location, order) {
  // Simplified cost estimation
  const baseCost = location.fulfillmentCostFactor || 1;
  return baseCost * order.line_items.reduce((total, item) => total + item.quantity, 0);
}

async function getShippingRates(order) {
  try {
    const carriers = await client.carrier.list();
    const fulfillmentLocations = await client.location.list({ type: 'fulfillment' });

    const rates = {};
    for (const location of fulfillmentLocations) {
      const locationRates = await Promise.all(carriers.map(carrier => 
        carrier.getRates({
          origin: location.address,
          destination: order.shipping_address,
          packages: convertOrderToPackages(order)
        })
      ));

      const flatRates = locationRates.flatMap(rate => rate);
      rates[location.id] = {
        allRates: flatRates,
        cheapestRate: flatRates.reduce((min, rate) => rate.price < min.price ? rate : min),
        fastestRate: flatRates.reduce((fastest, rate) => rate.deliveryDays < fastest.deliveryDays ? rate : fastest)
      };
    }

    return rates;
  } catch (error) {
    console.error("Error fetching shipping rates:", error);
    throw error;
  }
}

function convertOrderToPackages(order) {
  // Simplified conversion - in reality, this would involve a more complex packing algorithm
  return [{
    weight: order.line_items.reduce((total, item) => total + (item.weight * item.quantity), 0),
    dimensions: {
      length: Math.max(...order.line_items.map(item => item.length)),
      width: Math.max(...order.line_items.map(item => item.width)),
      height: Math.max(...order.line_items.map(item => item.height))
    }
  }];
}

async function getHistoricalFulfillmentData(customerId) {
  // In a real implementation, this would query a data warehouse or analytics service
  return client.analytics.getCustomerFulfillmentHistory(customerId);
}

async function getInventoryLevels(productIds) {
  const inventoryRecords = await client.inventory.list({ product_id: { in: productIds } });
  return inventoryRecords.reduce((acc, record) => {
    acc[record.location_id] = acc[record.location_id] || {};
    acc[record.location_id][record.product_id] = record.available_quantity;
    return acc;
  }, {});
}

async function logRoutingDecision(orderId, optimalLocation, mlPrediction) {
  await client.log.create({
    type: 'order_routing',
    order_id: orderId,
    selected_location: optimalLocation.id,
    ml_confidence: mlPrediction.confidence,
    routing_score: optimalLocation.score
  });
}

2.3 Order Status Management

Update and track order status throughout its lifecycle:

async function updateOrderStatus(orderId, newStatus) {
  try {
    const updatedOrder = await client.order.update(orderId, { status: newStatus });
    return updatedOrder;
  } catch (error) {
    console.error("Error updating order status:", error);
    throw error;
  }
}

3. Inventory Management

3.1 Global Inventory View

Create a function to get a global view of inventory across all locations:

async function getGlobalInventory(productId) {
  try {
    const inventoryList = await client.inventory.list({ product_id: productId });
    const globalInventory = inventoryList.reduce((acc, inv) => {
      acc[inv.location_id] = {
        quantity: inv.quantity,
        type: inv.inventory_type, // 'owned', 'dropship', or 'jit'
        safetyStock: inv.safety_stock_level
      };
      return acc;
    }, {});
    return globalInventory;
  } catch (error) {
    console.error("Error fetching global inventory:", error);
    throw error;
  }
}

3.2 Inventory Allocation

Allocate inventory for an order:

async function allocateInventory(orderId) {
  let allocatedItems = [];
  try {
    const order = await client.order.retrieve(orderId);
    const allocationResults = await Promise.all(order.line_items.map(async (item) => {
      const allocation = await determineOptimalAllocation(item, order.assigned_location);
      return { item, allocation };
    }));

    for (const { item, allocation } of allocationResults) {
      for (const alloc of allocation) {
        await client.inventory.allocate({
          product_id: item.product_id,
          location_id: alloc.location_id,
          quantity: alloc.quantity,
          allocation_type: alloc.type,
          order_id: orderId,
          item_id: item.id
        });
        allocatedItems.push({
          item_id: item.id,
          product_id: item.product_id,
          allocated_quantity: alloc.quantity,
          location_id: alloc.location_id,
          allocation_type: alloc.type
        });
      }
    }

    const fullyAllocated = allocatedItems.every(ai => 
      ai.allocated_quantity === order.line_items.find(li => li.id === ai.item_id).quantity
    );

    await client.order.update(orderId, { 
      status: fullyAllocated ? 'fully_allocated' : 'partially_allocated',
      inventory_allocations: allocatedItems
    });

    await logAllocationResult(orderId, allocatedItems, fullyAllocated);

    return { orderId, allocatedItems, fullyAllocated };
  } catch (error) {
    console.error(`Error allocating inventory for order ${orderId}:`, error);
    await client.order.update(orderId, { 
      status: 'allocation_failed',
      allocation_error: error.message
    });
    throw error;
  }
}

async function determineOptimalAllocation(item, preferredLocation) {
  const inventory = await getInventoryLevels(item.product_id);
  const reservations = await getActiveReservations(item.product_id);
  const safetyStockLevels = await getSafetyStockLevels(item.product_id);

  let remainingQuantity = item.quantity;
  let allocations = [];

  // Try to allocate from the preferred location first
  if (inventory[preferredLocation]) {
    const preferredAllocation = allocateFromLocation(
      preferredLocation, 
      remainingQuantity, 
      inventory[preferredLocation], 
      reservations[preferredLocation], 
      safetyStockLevels[preferredLocation]
    );
    allocations.push(preferredAllocation);
    remainingQuantity -= preferredAllocation.quantity;
  }

  // If we couldn't fully allocate from the preferred location, try others
  if (remainingQuantity > 0) {
    for (const [locationId, quantity] of Object.entries(inventory)) {
      if (locationId === preferredLocation) continue;
      
      const allocation = allocateFromLocation(
        locationId, 
        remainingQuantity, 
        quantity, 
        reservations[locationId], 
        safetyStockLevels[locationId]
      );
      
      if (allocation.quantity > 0) {
        allocations.push(allocation);
        remainingQuantity -= allocation.quantity;
        if (remainingQuantity === 0) break;
      }
    }
  }

  // If we still couldn't allocate everything, create a backorder
  if (remainingQuantity > 0) {
    allocations.push({
      location_id: 'backorder',
      quantity: remainingQuantity,
      type: 'backorder'
    });
  }

  return allocations;
}

function allocateFromLocation(locationId, requiredQuantity, availableQuantity, reservations, safetyStock) {
  const effectiveQuantity = Math.max(availableQuantity - reservations - safetyStock, 0);
  const allocatedQuantity = Math.min(requiredQuantity, effectiveQuantity);
  
  return {
    location_id: locationId,
    quantity: allocatedQuantity,
    type: allocatedQuantity > 0 ? 'standard' : 'none'
  };
}

async function getInventoryLevels(productId) {
  const inventoryRecords = await client.inventory.list({ product_id: productId });
  return inventoryRecords.reduce((acc, record) => {
    acc[record.location_id] = record.available_quantity;
    return acc;
  }, {});
}

async function getActiveReservations(productId) {
  const reservations = await client.reservation.list({ product_id: productId, status: 'active' });
  return reservations.reduce((acc, reservation) => {
    acc[reservation.location_id] = (acc[reservation.location_id] || 0) + reservation.quantity;
    return acc;
  }, {});
}

async function getSafetyStockLevels(productId) {
  const safetyStockRecords = await client.safetyStock.list({ product_id: productId });
  return safetyStockRecords.reduce((acc, record) => {
    acc[record.location_id] = record.quantity;
    return acc;
  }, {});
}

async function logAllocationResult(orderId, allocatedItems, fullyAllocated) {
  await client.log.create({
    type: 'inventory_allocation',
    order_id: orderId,
    allocated_items: allocatedItems,
    fully_allocated: fullyAllocated,
    timestamp: new Date().toISOString()
  });
}

3.3 Inventory Rebalancing

Implement a function to rebalance inventory across locations:

async function rebalanceInventory() {
  const rebalancingLog = [];
  try {
    const inventory = await client.inventory.list();
    const locations = await client.location.list();
    const salesVelocity = await getSalesVelocityByLocation();
    const seasonalTrends = await getSeasonalTrends();
    const shippingCosts = await getShippingCostMatrix();
    const storageCapacity = await getStorageCapacityByLocation();

    const rebalancingPlan = generateRebalancingPlan(
      inventory, 
      locations, 
      salesVelocity, 
      seasonalTrends, 
      shippingCosts, 
      storageCapacity
    );

    for (const transfer of rebalancingPlan) {
      try {
        await client.inventory.transfer(transfer);
        rebalancingLog.push({
          status: 'success',
          transfer: transfer
        });
      } catch (transferError) {
        console.error(`Error executing transfer:`, transferError);
        rebalancingLog.push({
          status: 'failed',
          transfer: transfer,
          error: transferError.message
        });
      }
    }

    const successfulTransfers = rebalancingLog.filter(log => log.status === 'success').length;
    console.log(`Inventory rebalancing completed. ${successfulTransfers}/${rebalancingPlan.length} transfers successful.`);

    await logRebalancingResult(rebalancingLog);

    return {
      totalTransfers: rebalancingPlan.length,
      successfulTransfers: successfulTransfers,
      rebalancingLog: rebalancingLog
    };

  } catch (error) {
    console.error("Error in inventory rebalancing process:", error);
    await logRebalancingError(error);
    throw error;
  }
}

function generateRebalancingPlan(inventory, locations, salesVelocity, seasonalTrends, shippingCosts, storageCapacity) {
  const rebalancingPlan = [];

  // Group inventory by product
  const productInventory = groupInventoryByProduct(inventory);

  for (const [productId, productLocations] of Object.entries(productInventory)) {
    const totalInventory = sum(Object.values(productLocations));
    const totalSalesVelocity = sum(Object.values(salesVelocity[productId] || {}));
    
    if (totalSalesVelocity === 0) continue; // Skip products with no sales

    const idealDistribution = calculateIdealDistribution(
      productId,
      totalInventory,
      locations,
      salesVelocity,
      seasonalTrends,
      storageCapacity
    );

    for (const [fromLocation, fromQuantity] of Object.entries(productLocations)) {
      const idealQuantity = idealDistribution[fromLocation] || 0;
      const difference = fromQuantity - idealQuantity;

      if (difference > 0) { // This location has excess inventory
        for (const [toLocation, toIdealQuantity] of Object.entries(idealDistribution)) {
          if (productLocations[toLocation] < toIdealQuantity) {
            const transferQuantity = Math.min(difference, toIdealQuantity - productLocations[toLocation]);
            if (transferQuantity > 0 && isTransferCostEffective(productId, fromLocation, toLocation, transferQuantity, shippingCosts)) {
              rebalancingPlan.push({
                product_id: productId,
                from_location: fromLocation,
                to_location: toLocation,
                quantity: transferQuantity
              });
              productLocations[fromLocation] -= transferQuantity;
              productLocations[toLocation] = (productLocations[toLocation] || 0) + transferQuantity;
            }
          }
        }
      }
    }
  }

  return rebalancingPlan;
}

function calculateIdealDistribution(productId, totalInventory, locations, salesVelocity, seasonalTrends, storageCapacity) {
  const distribution = {};
  const totalSalesVelocity = sum(Object.values(salesVelocity[productId] || {}));

  for (const location of locations) {
    const locationSalesVelocity = salesVelocity[productId]?.[location.id] || 0;
    const seasonalFactor = seasonalTrends[productId]?.[location.id] || 1;
    const adjustedSalesVelocity = locationSalesVelocity * seasonalFactor;

    let idealQuantity = Math.round((adjustedSalesVelocity / totalSalesVelocity) * totalInventory);
    idealQuantity = Math.min(idealQuantity, storageCapacity[location.id]); // Respect storage capacity

    distribution[location.id] = idealQuantity;
  }

  return distribution;
}

function isTransferCostEffective(productId, fromLocation, toLocation, quantity, shippingCosts) {
  const transferCost = shippingCosts[fromLocation]?.[toLocation] * quantity;
  const productValue = getProductValue(productId);
  const transferValueRatio = transferCost / (productValue * quantity);

  // Consider transfer cost-effective if it's less than 10% of the inventory value
  return transferValueRatio < 0.1;
}

function groupInventoryByProduct(inventory) {
  return inventory.reduce((acc, item) => {
    acc[item.product_id] = acc[item.product_id] || {};
    acc[item.product_id][item.location_id] = item.quantity;
    return acc;
  }, {});
}

async function getSalesVelocityByLocation() {
  const cacheKey = 'sales_velocity';
  let salesVelocity = cache.get(cacheKey);

  if (salesVelocity) {
    return salesVelocity;
  }

  try {
    // Assume we're fetching data for the last 90 days
    const endDate = new Date();
    const startDate = new Date(endDate.getTime() - (90 * 24 * 60 * 60 * 1000));

    const salesData = await client.analytics.getSales({
      start_date: startDate.toISOString(),
      end_date: endDate.toISOString(),
      group_by: ['product_id', 'location_id']
    });

    salesVelocity = salesData.reduce((acc, sale) => {
      acc[sale.product_id] = acc[sale.product_id] || {};
      acc[sale.product_id][sale.location_id] = sale.quantity / 90; // Daily sales velocity
      return acc;
    }, {});

    cache.set(cacheKey, salesVelocity);
    return salesVelocity;
  } catch (error) {
    console.error('Error fetching sales velocity:', error);
    throw new Error('Failed to fetch sales velocity data');
  }
}

async function getSeasonalTrends() {
  const cacheKey = 'seasonal_trends';
  let seasonalTrends = cache.get(cacheKey);

  if (seasonalTrends) {
    return seasonalTrends;
  }

  try {
    // Assume we're fetching seasonal data for the next 90 days
    const startDate = new Date();
    const endDate = new Date(startDate.getTime() + (90 * 24 * 60 * 60 * 1000));

    const trendsData = await client.analytics.getSeasonalTrends({
      start_date: startDate.toISOString(),
      end_date: endDate.toISOString(),
      group_by: ['product_id', 'location_id']
    });

    seasonalTrends = trendsData.reduce((acc, trend) => {
      acc[trend.product_id] = acc[trend.product_id] || {};
      acc[trend.product_id][trend.location_id] = trend.seasonal_factor;
      return acc;
    }, {});

    cache.set(cacheKey, seasonalTrends);
    return seasonalTrends;
  } catch (error) {
    console.error('Error fetching seasonal trends:', error);
    throw new Error('Failed to fetch seasonal trend data');
  }
}

async function getShippingCostMatrix() {
  const cacheKey = 'shipping_cost_matrix';
  let shippingCostMatrix = cache.get(cacheKey);

  if (shippingCostMatrix) {
    return shippingCostMatrix;
  }

  try {
    const locations = await client.location.list();
    shippingCostMatrix = {};

    for (const fromLocation of locations) {
      shippingCostMatrix[fromLocation.id] = {};
      for (const toLocation of locations) {
        if (fromLocation.id !== toLocation.id) {
          const shippingRate = await client.shipping.getRate({
            from: fromLocation.id,
            to: toLocation.id,
            weight: 1, // Assume 1 kg as a base weight
            volume: 1 // Assume 1 cubic meter as a base volume
          });
          shippingCostMatrix[fromLocation.id][toLocation.id] = shippingRate.cost;
        }
      }
    }

    cache.set(cacheKey, shippingCostMatrix);
    return shippingCostMatrix;
  } catch (error) {
    console.error('Error fetching shipping cost matrix:', error);
    throw new Error('Failed to fetch shipping cost data');
  }
}

async function getStorageCapacityByLocation() {
  const cacheKey = 'storage_capacity';
  let storageCapacity = cache.get(cacheKey);

  if (storageCapacity) {
    return storageCapacity;
  }

  try {
    const locations = await client.location.list();
    storageCapacity = {};

    for (const location of locations) {
      const capacityData = await client.warehouse.getCapacity(location.id);
      storageCapacity[location.id] = capacityData.available_capacity;
    }

    cache.set(cacheKey, storageCapacity);
    return storageCapacity;
  } catch (error) {
    console.error('Error fetching storage capacity:', error);
    throw new Error('Failed to fetch storage capacity data');
  }
}

function getProductValue(productId) {
  return new Promise((resolve, reject) => {
    const cacheKey = `product_value_${productId}`;
    const cachedValue = cache.get(cacheKey);

    if (cachedValue) {
      resolve(cachedValue);
    } else {
      client.product.get(productId)
        .then(product => {
          const value = product.cost || product.price || 0;
          cache.set(cacheKey, value);
          resolve(value);
        })
        .catch(error => {
          console.error(`Error fetching product value for ${productId}:`, error);
          reject(new Error(`Failed to fetch product value for ${productId}`));
        });
    }
  });
}

function sum(numbers) {
  return numbers.reduce((a, b) => a + b, 0);
}

async function logRebalancingResult(rebalancingLog) {
  await client.log.create({
    type: 'inventory_rebalancing',
    result: rebalancingLog,
    timestamp: new Date().toISOString()
  });
}

async function logRebalancingError(error) {
  await client.log.create({
    type: 'inventory_rebalancing_error',
    error: error.message,
    stack: error.stack,
    timestamp: new Date().toISOString()
  });
}

4. Fulfillment Orchestration

4.1 Fulfillment Order Creation

Generate fulfillment orders based on allocated inventory:

async function createFulfillmentOrder(orderId) {
  try {
    const order = await client.order.retrieve(orderId);
    const fulfillmentCenter = await selectFulfillmentCenter(order);
    
    const fulfillmentOrder = await client.fulfillmentOrder.create({
      order_id: orderId,
      location_id: fulfillmentCenter.id,
      line_items: order.line_items,
      picking_method: determineOptimalPickingMethod(order, fulfillmentCenter)
    });
    
    await client.order.update(orderId, { status: 'in_fulfillment' });
    
    return fulfillmentOrder;
  } catch (error) {
    console.error("Error creating fulfillment order:", error);
    throw error;
  }
}

function determineOptimalPickingMethod(order, fulfillmentCenter) {

  const orderSize = order.line_items.length;
  const orderVolume = order.line_items.reduce((total, item) => total + item.quantity, 0);
  const uniqueProducts = new Set(order.line_items.map(item => item.product_id)).size;

  if (fulfillmentCenter.capabilities.includes('wave_picking') && orderSize > 50) {
    return 'wave_picking';
  } else if (fulfillmentCenter.capabilities.includes('zone_picking') && uniqueProducts > 20) {
    return 'zone_picking';
  } else if (fulfillmentCenter.capabilities.includes('batch_picking') && orderVolume > 100) {
    return 'batch_picking';
  } else if (fulfillmentCenter.capabilities.includes('cluster_picking') && orderSize > 10 && orderSize <= 50) {
    return 'cluster_picking';
  } else {
    return 'discrete_order_picking'; // Default to single-order picking for smaller, simpler orders
  }
}

async function selectFulfillmentCenter(order) {
  // Implement logic to select the best fulfillment center based on various factors
  const fulfillmentCenters = await client.location.list({ type: 'fulfillment' });
  
  // Calculate scores for each fulfillment center
  const scoredCenters = await Promise.all(fulfillmentCenters.map(async (center) => {
    const inventoryAvailability = await checkInventoryAvailability(center.id, order.line_items);
    const shippingCost = await estimateShippingCost(center.id, order.shipping_address);
    const processingTime = estimateProcessingTime(center, order);
    
    const score = calculateScore(inventoryAvailability, shippingCost, processingTime);
    
    return { center, score };
  }));
  
  // Sort centers by score (descending) and return the best one
  const bestCenter = scoredCenters.sort((a, b) => b.score - a.score)[0].center;
  
  return bestCenter;
}

4.2 Fulfillment Tracking

Update and track fulfillment status:

async function updateFulfillmentStatus(fulfillmentOrderId, status, trackingInfo = null) {
  try {
    const updatedFulfillment = await client.fulfillmentOrder.update(fulfillmentOrderId, { 
      status: status,
      tracking_number: trackingInfo?.trackingNumber,
      tracking_url: trackingInfo?.trackingUrl
    });
    
    if (status === 'completed') {
      const order = await client.order.retrieve(updatedFulfillment.order_id);
      await client.order.update(order.id, { status: 'fulfilled' });
      await updateInventoryPostFulfillment(order);
    }
    
    await syncFulfillmentStatusWithWMS(fulfillmentOrderId, status);
    
    return updatedFulfillment;
  } catch (error) {
    console.error("Error updating fulfillment status:", error);
    throw error;
  }
}

async function syncFulfillmentStatusWithWMS(fulfillmentOrderId, status) {
  // Implement WMS integration logic
  try {
    const wmsClient = await getWMSClient();
    const wmsStatus = mapStatesetToWMSStatus(status);
    
    await wmsClient.updateFulfillmentStatus({
      externalOrderId: fulfillmentOrderId,
      status: wmsStatus
    });

    console.log(`Successfully synced fulfillment status ${status} for order ${fulfillmentOrderId} with WMS`);
  } catch (error) {
    console.error(`Error syncing fulfillment status with WMS for order ${fulfillmentOrderId}:`, error);
    // Consider implementing retry logic or alerting mechanism here
  }
}

async function updateInventoryPostFulfillment(order) {
  for (const item of order.line_items) {
    const fulfillment = await client.fulfillmentOrder.retrieve(item.fulfillment_order_id);
    if (fulfillment.status === 'completed') {
      await client.inventory.update({
        product_id: item.product_id,
        location_id: fulfillment.location_id,
        quantity: -item.quantity,
        reason: 'fulfillment'
      });
    }
  }

  // Update any reserved inventory
  const reservations = await client.reservation.list({ order_id: order.id });
  for (const reservation of reservations) {
    await client.reservation.delete(reservation.id);
  }

  console.log(`Inventory updated for order ${order.id} after successful fulfillment`);
}

4.3 Multi-Location Fulfillment

Handle split shipments from multiple locations:

async function createSplitFulfillment(orderId) {
  try {
    const order = await client.order.retrieve(orderId);
    const splitFulfillments = [];
    
    for (const item of order.line_items) {
      const availableLocations = await findAvailableLocations(item.product_id, item.quantity);
      
      for (const location of availableLocations) {
        const fulfillmentOrder = await client.fulfillmentOrder.create({
          order_id: orderId,
          location_id: location.id,
          line_items: [{ ...item, quantity: location.availableQuantity }]
        });
        splitFulfillments.push(fulfillmentOrder);
      }
    }
    
    await client.order.update(orderId, { status: 'split_fulfillment' });
    
    return splitFulfillments;
  } catch (error) {
    console.error("Error creating split fulfillment:", error);
    throw error;
  }
}

async function findAvailableLocations(productId, requiredQuantity) {
  // Implement logic to find locations with available inventory
}

5. Cross-Channel Synchronization

5.1 Inventory Sync

Sync inventory levels across all sales channels:

async function syncInventoryAcrossChannels() {
  try {
    const inventory = await client.inventory.list();
    const channels = await client.channel.list();
    
    for (const item of inventory) {
      for (const channel of channels) {
        await updateChannelInventory(channel.id, item.product_id, item.quantity);
      }
    }
    
    console.log("Inventory sync completed successfully");
  } catch (error) {
    console.error("Error during inventory sync:", error);
  }
}

async function updateChannelInventory(channelId, productId, quantity) {
  // Implement channel-specific inventory update logic
}

5.2 Order Status Sync

Sync order statuses across channels:

async function syncOrderStatus(orderId) {
  try {
    const order = await client.order.retrieve(orderId);
    await updateChannelOrderStatus(order.channel, order.channel_order_id, order.status);
  } catch (error) {
    console.error("Error syncing order status:", error);
    throw error;
  }
}

async function updateChannelOrderStatus(channelId, channelOrderId, status) {
  // Implement channel-specific order status update logic
}

6. Reporting and Analytics

6.1 Order Fulfillment Performance

Generate a report on order fulfillment performance:

async function generateFulfillmentPerformanceReport(startDate, endDate) {
  try {
    const orders = await client.order.list({
      created_at: { gte: startDate, lte: endDate },
      status: 'fulfilled'
    });

    const performance = orders.reduce((acc, order) => {
      const fulfillmentTime = calculateFulfillmentTime(order);
      const pickingEfficiency = calculatePickingEfficiency(order);
      acc.totalOrders++;
      acc.totalFulfillmentTime += fulfillmentTime;
      acc.totalPickingEfficiency += pickingEfficiency;
      acc.locations[order.assigned_location] = (acc.locations[order.assigned_location] || 0) + 1;
      return acc;
    }, { totalOrders: 0, totalFulfillmentTime: 0, totalPickingEfficiency: 0, locations: {} });

    performance.averageFulfillmentTime = performance.totalFulfillmentTime / performance.totalOrders;
    performance.averagePickingEfficiency = performance.totalPickingEfficiency / performance.totalOrders;

    return performance;
  } catch (error) {
    console.error("Error generating fulfillment performance report:", error);
    throw error;
  }
}

function calculateFulfillmentTime(order) {
  const receivedDate = new Date(order.created_at);
  const fulfilledDate = new Date(order.fulfilled_at);
  const fulfillmentTime = (fulfilledDate - receivedDate) / (1000 * 60 * 60); // Convert to hours
  return fulfillmentTime;
}

function calculatePickingEfficiency(order) {
  // Implement logic to calculate picking efficiency based on chosen picking method
  const pickingMethod = order.picking_method || 'single';
  let efficiency = 0;

  switch (pickingMethod) {
    case 'single':
      efficiency = calculateSingleOrderPickingEfficiency(order);
      break;
    case 'batch':
      efficiency = calculateBatchPickingEfficiency(order);
      break;
    case 'zone':
      efficiency = calculateZonePickingEfficiency(order);
      break;
    case 'wave':
      efficiency = calculateWavePickingEfficiency(order);
      break;
    default:
      console.warn(`Unknown picking method: ${pickingMethod}. Using default efficiency calculation.`);
      efficiency = calculateDefaultPickingEfficiency(order);
  }

  return efficiency;
}

6.2 Inventory Turnover Report

Generate a report on inventory turnover across locations:

async function generateInventoryReport(startDate, endDate) {
  try {
    const sales = await client.order.list({
      created_at: { gte: startDate, lte: endDate },
      status: 'fulfilled'
    });
    const currentInventory = await client.inventory.list();
    const historicalData = await getHistoricalInventoryData();

    const report = currentInventory.reduce((acc, inv) => {
      const sold = calculateTotalSold(sales, inv.product_id, inv.location_id);
      const turnoverRate = sold / ((inv.quantity + sold) / 2);
      const predictedDemand = predictFutureDemand(inv.product_id, historicalData);
      
      acc[inv.location_id] = acc[inv.location_id] || {};
      acc[inv.location_id][inv.product_id] = {
        currentStock: inv.quantity,
        soldQuantity: sold,
        turnoverRate: turnoverRate,
        predictedDemand: predictedDemand
      };
      return acc;
    }, {});

    return report;
  } catch (error) {
    console.error("Error generating inventory report:", error);
    throw error;
  }
}

function calculateTotalSold(sales, productId, locationId) {
  return sales.reduce((total, order) => {
    const relevantLineItems = order.line_items.filter(item => 
      item.product_id === productId && item.fulfillment_location_id === locationId
    );
    const quantitySold = relevantLineItems.reduce((sum, item) => sum + item.quantity, 0);
    return total + quantitySold;
  }, 0);
}

function predictFutureDemand(productId, historicalData) {
  // Implement demand forecasting logic using historical data
  const historicalSales = historicalData.filter(data => data.product_id === productId);
  
  if (historicalSales.length === 0) {
    return 0; // No historical data available
  }

  // Calculate average daily sales for the last 90 days
  const last90Days = historicalSales.slice(-90);
  const totalSales = last90Days.reduce((sum, day) => sum + day.quantity_sold, 0);
  const averageDailySales = totalSales / 90;

  // Apply simple exponential smoothing for short-term forecast
  const alpha = 0.3; // Smoothing factor
  let forecast = averageDailySales;
  for (let i = last90Days.length - 1; i >= 0; i--) {
    forecast = alpha * last90Days[i].quantity_sold + (1 - alpha) * forecast;
  }

  // Apply seasonal adjustment if applicable
  const seasonalIndex = calculateSeasonalIndex(historicalSales);
  forecast *= seasonalIndex;

  // Round to nearest integer and ensure non-negative
  return Math.max(Math.round(forecast), 0);
}

This Distributed Order Management Quickstart Guide focuses on the core aspects of Order Management, Inventory Control, and Fulfillment Orchestration. It provides a comprehensive set of functions to implement key DOM features such as unified order capture, intelligent routing, global inventory visibility, multi-location fulfillment, cross-channel synchronization, and performance reporting.