Order Bulk Operations

The Orders module supports high-volume operations through queued jobs with progress tracking, cancellation support, and PostgreSQL-optimized batch processing.

CSV Import

Starting an Import

POST /api/v1/orders/bulk/import
Content-Type: multipart/form-data

file: orders.csv
source: csv-import

Response:

{
  "data": {
    "id": "uuid",
    "source": "csv-import",
    "status": "processing",
    "total_rows": 500,
    "processed_rows": 0
  }
}

CSV Format

Column Required Description
order_number No Custom order number (auto-generated if empty)
customer_phone Yes* Customer phone (*or email required)
customer_email Yes* Customer email (*or phone required)
customer_first_name No Customer first name
customer_last_name No Customer last name
item_name Yes Product name
item_quantity Yes Quantity
item_price Yes Unit price
item_sku No Product SKU
total No Order total (auto-calculated if empty)
payment_status No Payment status (default: pending)
status No Order status (default: pending)
notes No Order notes

Checking Import Status

GET /api/v1/orders/bulk/import/{importId}

{
  "data": {
    "id": "uuid",
    "status": "processing",
    "total_rows": 500,
    "processed_rows": 250,
    "created_count": 240,
    "updated_count": 5,
    "skipped_count": 3,
    "error_count": 2,
    "errors": [
      { "row": 45, "message": "Missing customer phone/email" },
      { "row": 128, "message": "Invalid payment_status value" }
    ]
  }
}

Import progress is also broadcast via Laravel Reverb for real-time UI updates.

Cancelling an Import

POST /api/v1/orders/bulk/import/{importId}/cancel

Sets import status to cancelled. The background job checks this flag between chunks and stops processing.

Processing Details

  • CSV is parsed and processed in 500-row chunks
  • Each chunk runs through the OrderIngestService for proper validation and event publishing
  • Uses PostgreSQL INSERT ... ON CONFLICT DO UPDATE for idempotent upserts
  • Queued on the imports queue (configure a dedicated Horizon supervisor)

CSV Export

Starting an Export

POST /api/v1/orders/bulk/export

{
  "filters": {
    "status": "delivered",
    "date_from": "2026-01-01",
    "date_to": "2026-03-01"
  },
  "columns": ["order_number", "customer_name", "total", "status", "created_at"]
}

Downloading the Export

GET /api/v1/orders/bulk/export/{exportId}/download

Returns the CSV file. Exports use cursor-based streaming for memory efficiency — even millions of orders won't exhaust server memory.

Bulk Status Update

Update multiple orders' status in a single request:

POST /api/v1/orders/bulk/status

{
  "order_ids": ["uuid1", "uuid2", "uuid3"],
  "status": "confirmed",
  "reason": "Payment verified"
}

Internally uses a single UPDATE ... WHERE id IN (...) SQL query plus batch-inserted status history records for performance.

Bulk Ship

Create shipments for multiple orders at once:

POST /api/v1/orders/bulk/ship

{
  "shipments": [
    {
      "order_id": "uuid1",
      "carrier_module": "shipping-delhivery",
      "awb_number": "DLV123456",
      "tracking_url": "https://track.delhivery.com/DLV123456"
    },
    {
      "order_id": "uuid2",
      "carrier_module": "shipping-delhivery",
      "awb_number": "DLV123457",
      "tracking_url": "https://track.delhivery.com/DLV123457"
    }
  ]
}

Bulk Cancel

Cancel multiple orders at once:

POST /api/v1/orders/bulk/cancel

{
  "order_ids": ["uuid1", "uuid2"],
  "reason": "Out of stock"
}

Only cancels orders in cancellable statuses (pending, confirmed, processing).

Queue Configuration

Add dedicated supervisors in config/horizon.php:

'environments' => [
    'production' => [
        'supervisor-imports' => [
            'connection' => 'redis',
            'queue' => ['imports'],
            'maxProcesses' => 2,
            'timeout' => 600,
        ],
        'supervisor-exports' => [
            'connection' => 'redis',
            'queue' => ['exports'],
            'maxProcesses' => 2,
            'timeout' => 300,
        ],
    ],
],