Skip to content

On‐demand Delivery Services

Agilasoft Cloud Technologies edited this page Jan 9, 2026 · 1 revision

ODDS (On-demand Delivery Services) User Guide

Table of Contents

  1. Overview
  2. Setup and Installation
  3. Configuration
  4. Using ODDS
  5. API Reference
  6. Webhook Configuration
  7. Provider-Specific Guides
  8. Troubleshooting
  9. Best Practices

Overview

The ODDS (On-demand Delivery Services) system provides a unified interface for integrating with multiple on-demand delivery service providers. It is part of the Transport module and supports:

  • Lalamove ✅ (Fully implemented)
  • Transportify (Ready for implementation)
  • GrabExpress (Ready for implementation)
  • PandaGo (Ready for implementation)
  • NinjaVAN (Ready for implementation)

Key Features

  • Unified Interface: Same API for all providers
  • Multi-Provider Support: Switch between providers easily
  • Automatic Quotation Caching: Quotations are cached for reuse
  • Real-time Status Updates: Webhook integration for status changes
  • Cross-Module Support: Works with Transport, Warehousing, Air Freight, and Sea Freight modules

Setup and Installation

Prerequisites

  • Frappe/ERPNext installation
  • Logistics app installed
  • Transport module enabled
  • API credentials from your chosen ODDS provider(s)

Installation Steps

  1. Ensure ODDS module is available

    • The ODDS system is automatically included with the Transport module
    • No additional installation required
  2. Create ODDS Settings

    • Navigate to: Transport > ODDS Settings
    • Or search for "ODDS Settings" in the search bar
  3. Install Doctypes (if not already installed)

    bench --site [your-site] migrate

Configuration

Step 1: Access ODDS Settings

  1. Go to Transport > ODDS Settings
  2. Or search for "ODDS Settings" in the search bar

Step 2: Configure General Settings

  1. Enable ODDS Integration: Check the "ODDS Integration Enabled" checkbox
  2. Set Default Provider: Choose your default provider from the dropdown:
    • lalamove
    • transportify
    • grabexpress
    • pandago
    • ninjavan

Step 3: Configure Provider Settings

Lalamove Configuration

  1. Enable Lalamove: Check "Enabled" under Lalamove Settings
  2. API Key: Enter your Lalamove API key
  3. API Secret: Enter your Lalamove API secret (stored encrypted)
  4. Environment: Select "Sandbox" for testing or "Production" for live use
  5. Market Code: Enter your market code (e.g., HK_HKG, SG_SIN, TH_BKK)
  6. Webhook Secret: (Optional) Enter webhook secret for webhook validation

Other Providers

Configure other providers similarly when they are implemented:

  • Transportify
  • GrabExpress
  • PandaGo
  • NinjaVAN

Step 4: Save Settings

Click Save to store your configuration.


Using ODDS

Method 1: Using the UI (Recommended)

Getting a Quotation

  1. Open a supported document (e.g., Transport Order, Transport Job, Warehouse Job)
  2. Look for the ODDS section or button
  3. Click Get Quotation
  4. Select your provider (or use default)
  5. Review the quotation details:
    • Price
    • Currency
    • Distance
    • Service type
    • Expiration time

Creating an Order

  1. After getting a quotation, click Create Order
  2. Confirm the order details
  3. The order will be created with the selected provider
  4. Track the order status in real-time

Method 2: Using API Endpoints

Get Quotation

import frappe

result = frappe.call(
    "logistics.transport.odds.api.get_odds_quotation",
    doctype="Transport Order",
    docname="TO-00001",
    provider="lalamove"  # Optional, uses default if not provided
)

if result["success"]:
    quotation = result["data"]
    print(f"Price: {quotation['price']} {quotation['currency']}")
    print(f"Quotation ID: {quotation['quotation_id']}")
else:
    print(f"Error: {result['error']}")

Create Order

import frappe

result = frappe.call(
    "logistics.transport.odds.api.create_odds_order",
    doctype="Transport Order",
    docname="TO-00001",
    quotation_id=None,  # Optional, will get new quotation if None
    provider="lalamove"  # Optional
)

if result["success"]:
    order = result["data"]
    print(f"Order ID: {order['order_id']}")
    print(f"Status: {order['status']}")
else:
    print(f"Error: {result['error']}")

Get Order Status

import frappe

result = frappe.call(
    "logistics.transport.odds.api.get_odds_order_status",
    order_id="123456789",
    provider="lalamove"
)

if result["success"]:
    order = result["data"]
    print(f"Status: {order['status']}")
    print(f"Driver: {order.get('driver', {}).get('name', 'N/A')}")

Sync Order Status

import frappe

result = frappe.call(
    "logistics.transport.odds.api.sync_odds_order_status",
    order_id="123456789",
    provider="lalamove"
)

Cancel Order

import frappe

result = frappe.call(
    "logistics.transport.odds.api.cancel_odds_order",
    order_id="123456789",
    provider="lalamove"
)

Method 3: Using Python Service Directly

from logistics.transport.odds.service import ODDSService

# Initialize service with provider
service = ODDSService(provider_code="lalamove")

# Get quotation
quotation = service.get_quotation("Transport Order", "TO-00001")
print(f"Price: {quotation['price']}")

# Create order
order = service.create_order("Transport Order", "TO-00001")
print(f"Order ID: {order['order_id']}")

# Get order details
order_details = service.get_order_details(order['order_id'])

# Sync status
service.sync_order_status(order['order_id'])

# Cancel order
service.cancel_order(order['order_id'])

API Reference

Available Endpoints

All endpoints are whitelisted and can be called from client-side scripts or server-side code.

1. get_odds_quotation

Get a delivery quotation for a document.

Parameters:

  • doctype (required): Source doctype (e.g., "Transport Order")
  • docname (required): Source document name
  • provider (optional): Provider code (defaults to settings default)

Returns:

{
    "success": true,
    "data": {
        "quotation_id": "123456789",
        "price": 50.00,
        "currency": "HKD",
        "expires_at": "2025-01-01T12:00:00Z",
        "service_type": "VAN",
        "distance": 5.2
    }
}

2. create_odds_order

Create a delivery order.

Parameters:

  • doctype (required): Source doctype
  • docname (required): Source document name
  • quotation_id (optional): Quotation ID (will get new if not provided)
  • provider (optional): Provider code

Returns:

{
    "success": true,
    "data": {
        "order_id": "987654321",
        "status": "ASSIGNING_DRIVER",
        "price": 50.00,
        "currency": "HKD"
    },
    "message": "ODDS order created successfully"
}

3. get_odds_order_status

Get current order status.

Parameters:

  • order_id (required): ODDS order ID
  • provider (optional): Provider code

Returns:

{
    "success": true,
    "data": {
        "order_id": "987654321",
        "status": "ON_GOING",
        "driver": {
            "name": "John Doe",
            "phone": "+85212345678"
        }
    }
}

4. sync_odds_order_status

Manually sync order status from provider.

Parameters:

  • order_id (required): ODDS order ID
  • provider (optional): Provider code

5. cancel_odds_order

Cancel an order.

Parameters:

  • order_id (required): ODDS order ID
  • provider (optional): Provider code

6. get_available_providers

Get list of available providers.

Returns:

{
    "success": true,
    "data": [
        {"code": "lalamove", "name": "Lalamove"},
        {"code": "transportify", "name": "Transportify"}
    ]
}

Webhook Configuration

Setting Up Webhooks

Webhooks allow real-time status updates from ODDS providers.

For Lalamove

  1. Get Webhook URL

    • Your webhook URL: https://your-domain.com/api/method/logistics.transport.odds.webhook.handle_webhook?provider=lalamove
  2. Configure in Lalamove Dashboard

    • Log in to Lalamove Developer Dashboard
    • Navigate to Webhook Settings
    • Enter the webhook URL
    • Configure events to subscribe to:
      • ORDER_STATUS_CHANGED
      • DRIVER_ASSIGNED
      • ORDER_AMOUNT_CHANGED
      • ORDER_REPLACED
      • ORDER_EDITED
  3. Set Webhook Secret

    • In ODDS Settings, enter the webhook secret
    • This is used to validate incoming webhooks
  4. Test Webhook

    • Use Lalamove's webhook testing tool
    • Verify that status updates are received

Webhook Events Handled

  • ORDER_STATUS_CHANGED: Order status updated
  • DRIVER_ASSIGNED: Driver assigned to order
  • ORDER_AMOUNT_CHANGED: Order price changed
  • ORDER_REPLACED: Order replaced with new order
  • ORDER_EDITED: Order details edited

Webhook Security

  • Webhooks are validated using HMAC signatures
  • Invalid signatures are rejected
  • Webhook secret should be kept secure

Provider-Specific Guides

Lalamove

Supported Features

  • ✅ Quotation retrieval
  • ✅ Order creation
  • ✅ Order cancellation
  • ✅ Order status tracking
  • ✅ Driver information
  • ✅ Multiple stops (up to 15)
  • ✅ Scheduled deliveries
  • ✅ Webhook integration

Service Types

  • MOTORCYCLE: For small, lightweight items
  • VAN: For medium-sized items
  • TRUCK: For large items or bulk deliveries

Market Codes

Common market codes:

  • HK_HKG: Hong Kong
  • SG_SIN: Singapore
  • TH_BKK: Thailand
  • MY_KUL: Malaysia
  • PH_MNL: Philippines

API Limits

  • Quotation requests: 300/minute
  • Order creation: 30/minute
  • Order status: 300/minute

Best Practices

  1. Quotation Validity: Quotations expire in 5 minutes
  2. Order Creation: Create orders immediately after getting quotation
  3. Status Updates: Use webhooks for real-time updates
  4. Error Handling: Always check for quotation expiration

Other Providers

Configuration guides for other providers will be added as they are implemented.


Troubleshooting

Common Issues

1. "Provider not supported" Error

Problem: Provider code is not recognized.

Solution:

  • Check that provider is registered in the system
  • Verify provider code spelling (case-insensitive)
  • Ensure provider module is properly imported

2. "Authentication failed" Error

Problem: API credentials are incorrect.

Solution:

  • Verify API key and secret in ODDS Settings
  • Check that provider is enabled
  • Ensure credentials match the selected environment (sandbox/production)

3. "Quotation expired" Error

Problem: Quotation has expired (valid for 5 minutes).

Solution:

  • Get a new quotation
  • Create order immediately after getting quotation
  • Use auto_get_quotation=True when creating orders

4. "Address does not have coordinates" Error

Problem: Address is missing latitude/longitude.

Solution:

  • Geocode the address first
  • Ensure address has valid coordinates
  • Use the address geocoding feature in the system

5. Webhook Not Receiving Updates

Problem: Webhooks are not being received.

Solution:

  • Verify webhook URL is correct and accessible
  • Check webhook secret matches provider configuration
  • Ensure webhook is enabled in provider dashboard
  • Check server logs for webhook errors

6. Order Status Not Updating

Problem: Order status is not syncing.

Solution:

  • Manually sync using sync_odds_order_status API
  • Check webhook configuration
  • Verify order ID is correct
  • Check provider API status

Debugging

Enable Debug Logging

import frappe

# Enable debug mode
frappe.conf.developer_mode = 1

# Check logs
frappe.log_error("Debug message", "ODDS Debug")

Check ODDS Order Record

  1. Go to Transport > ODDS Order
  2. Search for your order ID
  3. Check the "Order Data" field for raw provider response
  4. Review "Error Message" field if present

Check ODDS Quotation Record

  1. Go to Transport > ODDS Quotation
  2. Search for quotation ID
  3. Verify quotation is still valid
  4. Check expiration time

Best Practices

1. Quotation Management

  • Get quotations on-demand: Don't cache quotations for too long
  • Handle expiration: Always check quotation validity before creating orders
  • Use cached quotations: System automatically caches valid quotations

2. Order Creation

  • Create immediately: Create orders right after getting quotation
  • Handle errors: Implement retry logic for failed orders
  • Validate data: Ensure all required fields are present before creating order

3. Status Tracking

  • Use webhooks: Prefer webhooks over polling for status updates
  • Sync periodically: Set up periodic sync for critical orders
  • Handle all statuses: Implement handlers for all possible order statuses

4. Error Handling

  • Graceful degradation: Handle provider unavailability gracefully
  • Retry logic: Implement retry for transient errors
  • User feedback: Provide clear error messages to users

5. Security

  • Protect credentials: Never expose API keys/secrets
  • Validate webhooks: Always validate webhook signatures
  • Use HTTPS: Ensure webhook URLs use HTTPS
  • Limit access: Restrict ODDS Settings access to authorized users

6. Performance

  • Cache quotations: Reuse valid quotations when possible
  • Batch operations: Group multiple operations when possible
  • Async processing: Use background jobs for non-critical operations

7. Monitoring

  • Track orders: Monitor order creation and completion rates
  • Log errors: Log all errors for debugging
  • Monitor webhooks: Track webhook delivery success rate
  • Alert on failures: Set up alerts for critical failures

Examples

Example 1: Complete Order Flow

from logistics.transport.odds.service import ODDSService

# Initialize service
service = ODDSService(provider_code="lalamove")

# Step 1: Get quotation
quotation = service.get_quotation("Transport Order", "TO-00001")
print(f"Quotation: {quotation['price']} {quotation['currency']}")

# Step 2: Create order
order = service.create_order(
    "Transport Order", 
    "TO-00001",
    quotation_id=quotation['quotation_id']
)
print(f"Order created: {order['order_id']}")

# Step 3: Monitor status
status = service.get_order_details(order['order_id'])
print(f"Status: {status['status']}")

# Step 4: Sync status (if needed)
service.sync_order_status(order['order_id'])

Example 2: Handling Quotation Expiration

from logistics.transport.odds.service import ODDSService
from logistics.transport.odds.exceptions import ODDSQuotationExpiredException

service = ODDSService(provider_code="lalamove")

try:
    order = service.create_order("Transport Order", "TO-00001")
except ODDSQuotationExpiredException:
    # Quotation expired, get new one and retry
    quotation = service.get_quotation("Transport Order", "TO-00001")
    order = service.create_order(
        "Transport Order", 
        "TO-00001",
        quotation_id=quotation['quotation_id']
    )

Example 3: Using Different Providers

from logistics.transport.odds.service import ODDSService

# Use Lalamove
lalamove_service = ODDSService(provider_code="lalamove")
lalamove_order = lalamove_service.create_order("Transport Order", "TO-00001")

# Use Transportify (when implemented)
transportify_service = ODDSService(provider_code="transportify")
transportify_order = transportify_service.create_order("Transport Order", "TO-00001")

Example 4: Client-Side Integration

// Get quotation
frappe.call({
    method: 'logistics.transport.odds.api.get_odds_quotation',
    args: {
        doctype: 'Transport Order',
        docname: 'TO-00001',
        provider: 'lalamove'
    },
    callback: function(r) {
        if (r.message.success) {
            console.log('Quotation:', r.message.data);
        } else {
            frappe.show_alert({
                message: 'Error: ' + r.message.error,
                indicator: 'red'
            });
        }
    }
});

// Create order
frappe.call({
    method: 'logistics.transport.odds.api.create_odds_order',
    args: {
        doctype: 'Transport Order',
        docname: 'TO-00001',
        provider: 'lalamove'
    },
    callback: function(r) {
        if (r.message.success) {
            frappe.show_alert({
                message: 'Order created successfully!',
                indicator: 'green'
            });
        }
    }
});

Support and Resources

Documentation

  • ODDS README: logistics/transport/odds/README.md
  • API Documentation: See API Reference section above
  • Provider Documentation: Check provider-specific guides

Getting Help

  1. Check Troubleshooting: Review common issues above
  2. Review Logs: Check error logs in Frappe
  3. Provider Support: Contact provider support for API issues
  4. System Administrator: Contact your system administrator for system issues

Additional Resources


Changelog

Version 1.0 (Initial Release)

  • ✅ Generic ODDS system implementation
  • ✅ Lalamove provider fully implemented
  • ✅ Multi-provider support structure
  • ✅ Unified API endpoints
  • ✅ Webhook integration
  • ✅ Quotation caching
  • ✅ Order tracking

Last Updated: January 2025
Version: 1.0
Module: Transport

Getting Started

Setup and Settings

Sea Freight

Air Freight

Transport

Customs

Warehousing

Pricing Center

Job Management

Sustainability

Intercompany

Special Projects

Pages

Features

Reports

Glossary

Clone this wiki locally