Skip to content

Odoo Sync Workflow - Developer Guide

Complete code examples for synchronizing subscription and payment data between Odoo ERP and the ABS Platform.

Overview

The Odoo sync workflow handles: - Subscription state synchronization from Odoo to ABS - Payment status validation - Plan activation based on payment confirmation - Billing milestone reporting back to Odoo


Why Odoo Sync Matters

Odoo ERP is the source of truth for: - ✅ Payment status (paid, not_paid, partial, in_payment, etc.) - ✅ Subscription state (in_progress, draft, to_renew, closed, cancel) - ✅ Invoice generation and billing cycles - ✅ Customer account management

ABS Platform uses this data to: - ✅ Activate/deactivate service plans - ✅ Block service for overdue payments - ✅ Track quota top-ups - ✅ Report usage for billing


Phase 0: Odoo Subscription Sync

Purpose

Sync subscription and payment data from Odoo ERP before any service begins. This is Phase 0 - the REQUIRED starting point for both rider and attendant workflows.

Basic Subscription Sync (Successful Payment)

Publish Topic:

emit/odo/subscription/plan/bss-plan-weekly-freedom-nairobi-v2-plan1/sync

Payload:

{
  "timestamp": "2025-01-15T08:00:00Z",
  "plan_id": "bss-plan-weekly-freedom-nairobi-v2-plan1",
  "correlation_id": "odoo-sync-001",
  "actor": {
    "type": "system",
    "id": "odoo-erp"
  },
  "data": {
    "action": "SYNC_ODOO_SUBSCRIPTION",
    "odoo_subscription_id": 12345,
    "odoo_payment_state": "paid",
    "odoo_subscription_state": "in_progress",
    "odoo_currency_id": "USD",
    "odoo_amount_total": 99.99
  }
}

Subscribe Topic:

echo/odo/subscription/plan/+/sync

Expected Response:

{
  "signals": ["ODOO_SYNC_SUCCESS"],
  "metadata": {
    "fsm_inputs_generated": [
      { "cycle": "payment_cycle", "input": "CONTRACT_SIGNED" },
      { "cycle": "payment_cycle", "input": "DEPOSIT_PAID" },
      { "cycle": "service_cycle", "input": "DEPOSIT_CONFIRMED" }
    ],
    "odoo_last_sync_at": "2025-01-15T08:00:00Z",
    "payment_state": "paid",
    "subscription_state": "in_progress"
  }
}

What Happens: 1. ✅ Agent state updated with Odoo data 2. ✅ FSM inputs generated for payment/service cycles 3. ✅ FSM transitions: payment_cycle: INITIAL → DEPOSIT_DUE → CURRENT 4. ✅ FSM transitions: service_cycle: INITIAL → WAIT_BATTERY_ISSUE 5. ✅ Service ready for use (payment confirmed)


Payment State Matrix

Complete Payment State Reference

Payment State Subscription State Service Allowed? FSM Inputs Generated
paid in_progress ✅ Yes CONTRACT_SIGNED, DEPOSIT_PAID, DEPOSIT_CONFIRMED
partial in_progress ⏳ Wait None (waiting for full payment)
in_payment in_progress ⏳ Wait None (waiting for confirmation)
not_paid in_progress ❌ No SUBSCRIPTION_EXPIRED
cancel in_progress ❌ No SUBSCRIPTION_EXPIRED
reversed in_progress ❌ No SUBSCRIPTION_EXPIRED
paid draft ❌ No None
paid to_renew ⚠️ Grace Period RENEWAL_REQUIRED, CONTINUE_SERVICE_REQUESTED
paid closed ❌ No SERVICE_TERMINATION_REQUESTED
paid cancel ❌ No SERVICE_TERMINATION_REQUESTED

Service Logic:

const isActiveSubscription = odoo_subscription_state === 'in_progress';
const isPaymentConfirmed = odoo_payment_state === 'paid';  // Only FULL payment
const serviceAllowed = isActiveSubscription && isPaymentConfirmed;


Sync Scenarios

Scenario 1: Overdue Payment

Customer has not paid their subscription.

Publish Topic:

emit/odo/subscription/plan/bss-plan-weekly-freedom-nairobi-v2-plan1/sync_overdue

Payload:

{
  "timestamp": "2025-01-15T08:05:00Z",
  "plan_id": "bss-plan-weekly-freedom-nairobi-v2-plan1",
  "correlation_id": "odoo-sync-002",
  "actor": {
    "type": "system",
    "id": "odoo-erp"
  },
  "data": {
    "action": "SYNC_ODOO_SUBSCRIPTION",
    "odoo_subscription_id": 12346,
    "odoo_payment_state": "not_paid",
    "odoo_subscription_state": "in_progress"
  }
}

Expected Response:

{
  "signals": ["ODOO_SYNC_SUCCESS"],
  "metadata": {
    "fsm_inputs_generated": [
      { "cycle": "payment_cycle", "input": "SUBSCRIPTION_EXPIRED" }
    ],
    "payment_state": "not_paid",
    "subscription_state": "in_progress"
  }
}

Result: ❌ Service blocked until payment resolved.


Scenario 2: Partial Payment

Customer has made partial payment, waiting for full amount.

Publish Topic:

emit/odo/subscription/plan/bss-plan-weekly-freedom-nairobi-v2-plan1/sync_partial

Payload:

{
  "timestamp": "2025-01-15T08:09:00Z",
  "plan_id": "bss-plan-weekly-freedom-nairobi-v2-plan1",
  "correlation_id": "odoo-sync-004",
  "actor": {
    "type": "system",
    "id": "odoo-erp"
  },
  "data": {
    "action": "SYNC_ODOO_SUBSCRIPTION",
    "odoo_subscription_id": 12348,
    "odoo_payment_state": "partial",
    "odoo_subscription_state": "in_progress",
    "odoo_currency_id": "USD",
    "odoo_amount_total": 99.99,
    "odoo_amount_paid": 50.00
  }
}

Expected Response:

{
  "signals": ["ODOO_SYNC_SUCCESS"],
  "metadata": {
    "fsm_inputs_generated": [],
    "payment_state": "partial",
    "subscription_state": "in_progress",
    "payment_partial": true
  }
}

Result: ⏳ Waiting for full payment confirmation.


Scenario 3: Payment Processing

Payment is being processed (waiting for confirmation).

Publish Topic:

emit/odo/subscription/plan/bss-plan-weekly-freedom-nairobi-v2-plan1/sync_in_payment

Payload:

{
  "timestamp": "2025-01-15T08:07:00Z",
  "plan_id": "bss-plan-weekly-freedom-nairobi-v2-plan1",
  "correlation_id": "odoo-sync-003",
  "actor": {
    "type": "system",
    "id": "odoo-erp"
  },
  "data": {
    "action": "SYNC_ODOO_SUBSCRIPTION",
    "odoo_subscription_id": 12347,
    "odoo_payment_state": "in_payment",
    "odoo_subscription_state": "in_progress",
    "odoo_currency_id": "USD",
    "odoo_amount_total": 99.99
  }
}

Result: ⏳ System waits for payment confirmation. Next sync with paid status will allow service.


Scenario 4: Subscription Renewal Due

Subscription approaching renewal date.

Publish Topic:

emit/odo/subscription/plan/service-plan-basic-newest-k/sync_renewal

Payload:

{
  "timestamp": "2025-01-15T08:17:00Z",
  "plan_id": "service-plan-basic-newest-k",
  "correlation_id": "odoo-sync-008",
  "actor": {
    "type": "system",
    "id": "odoo-erp"
  },
  "data": {
    "action": "SYNC_ODOO_SUBSCRIPTION",
    "odoo_subscription_id": 12352,
    "odoo_payment_state": "paid",
    "odoo_subscription_state": "to_renew",
    "odoo_currency_id": "USD",
    "odoo_amount_total": 99.99
  }
}

Expected Response:

{
  "signals": ["ODOO_SYNC_SUCCESS"],
  "metadata": {
    "fsm_inputs_generated": [
      { "cycle": "payment_cycle", "input": "RENEWAL_REQUIRED" },
      { "cycle": "service_cycle", "input": "CONTINUE_SERVICE_REQUESTED" }
    ],
    "payment_state": "paid",
    "subscription_state": "to_renew",
    "renewal_required": true
  }
}

Result: ⚠️ Subscription needs renewal. Service may continue with grace period.


Scenario 5: Cancelled Subscription

Subscription has been cancelled by customer or admin.

Publish Topic:

emit/odo/subscription/plan/service-plan-basic-newest-k/sync_subscription_cancel

Payload:

{
  "timestamp": "2025-01-15T08:21:00Z",
  "plan_id": "service-plan-basic-newest-k",
  "correlation_id": "odoo-sync-010",
  "actor": {
    "type": "system",
    "id": "odoo-erp"
  },
  "data": {
    "action": "SYNC_ODOO_SUBSCRIPTION",
    "odoo_subscription_id": 12354,
    "odoo_payment_state": "paid",
    "odoo_subscription_state": "cancel"
  }
}

Expected FSM Inputs:

[
  { "cycle": "service_cycle", "input": "SERVICE_TERMINATION_REQUESTED" }
]

Result: ❌ Subscription cancelled. Service permanently blocked.


Usage Reporting to Odoo

Report Completed Service

After service completion, report usage back to Odoo for billing.

Publish Topic:

emit/uxi/billing/plan/bss-plan-weekly-freedom-nairobi-v2-plan1/usage_report

Payload:

{
  "timestamp": "2025-01-15T11:05:00Z",
  "plan_id": "bss-plan-weekly-freedom-nairobi-v2-plan1",
  "correlation_id": "att-usage-report-001",
  "actor": {
    "type": "attendant",
    "id": "attendant-001"
  },
  "data": {
    "action": "REPORT_SERVICE_USAGE_TO_ODOO",
    "usage_type": "battery_swap_completed",
    "service_completion_details": {
      "old_battery_id": "BAT_RETURN_ATT_001",
      "new_battery_id": "BAT_NEW_ATT_001",
      "energy_transferred": 48.5,
      "service_duration": 240,
      "attendant_station": "STATION_001"
    }
  }
}

Subscribe Topics:

echo/uxi/billing/plan/+/usage_report
echo/odo/billing/plan/+/billing_processed

Secondary Emission (automatic - ABS to Odoo): - Topic: emit/abs/billing/plan/bss-plan-weekly-freedom-nairobi-v2-plan1/swap_completed - Expected Echo: echo/odo/billing/plan/bss-plan-weekly-freedom-nairobi-v2-plan1/billing_processed

What Happens: 1. ✅ ABS receives usage report 2. ✅ ABS emits secondary message to Odoo 3. ✅ Odoo processes billing milestone 4. ✅ Odoo confirms with echo message 5. ✅ Invoice generated (if applicable)


Process Billing Echo

Handle billing confirmation from Odoo.

Subscribe Topic:

echo/odo/billing/plan/+/processed

Note: This message comes FROM Odoo (you don't publish it).

Expected Message from Odoo:

{
  "timestamp": "2025-01-15T08:51:00Z",
  "plan_id": "bss-plan-weekly-freedom-nairobi-v2-plan1",
  "correlation_id": "usage-report-001",
  "actor": {
    "type": "system",
    "id": "odoo-billing"
  },
  "data": {
    "action": "PROCESS_ODOO_BILLING_ECHO",
    "echo_data": {
      "correlation_id": "usage-report-001",
      "billing_status": "processed",
      "invoice_id": "INV-2025-01-001",
      "invoice_amount": 10.00
    }
  }
}

ABS Processing:

{
  "signals": ["ODOO_BILLING_COMPLETED", "ECHO_PROCESSED", "INVOICE_GENERATED"],
  "metadata": {
    "billing_result": {
      "status": "processed",
      "invoice_id": "INV-2025-01-001"
    },
    "echo_processing": {
      "correlation_id": "usage-report-001",
      "pending_operation_cleaned": true
    }
  }
}


Complete End-to-End Example

Odoo Sync Lifecycle Script

#!/bin/bash
# Complete Odoo sync workflow

PLAN_ID="bss-plan-weekly-freedom-nairobi-v2-plan1"

echo "===== ODOO SYNC WORKFLOW ====="
echo "Plan: $PLAN_ID"
echo ""

# Step 1: Initial subscription sync (activates plan)
echo "Step 1: Syncing subscription from Odoo..."
mosquitto_pub -h localhost -t "emit/odo/subscription/plan/$PLAN_ID/sync" -m "{
  \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",
  \"plan_id\": \"$PLAN_ID\",
  \"correlation_id\": \"odoo-lifecycle-001\",
  \"actor\": {\"type\": \"system\", \"id\": \"odoo-erp\"},
  \"data\": {
    \"action\": \"SYNC_ODOO_SUBSCRIPTION\",
    \"odoo_subscription_id\": 12345,
    \"odoo_payment_state\": \"paid\",
    \"odoo_subscription_state\": \"in_progress\",
    \"odoo_currency_id\": \"USD\",
    \"odoo_amount_total\": 99.99
  }
}"
sleep 3

# Step 2: Service completion (simulated)
echo "Step 2: Service completed (simulated)..."
# (See rider or attendant workflow for actual service)
sleep 2

# Step 3: Report usage to Odoo
echo "Step 3: Reporting usage to Odoo..."
mosquitto_pub -h localhost -t "emit/uxi/billing/plan/$PLAN_ID/usage_report" -m "{
  \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",
  \"plan_id\": \"$PLAN_ID\",
  \"correlation_id\": \"odoo-lifecycle-002\",
  \"actor\": {\"type\": \"system\", \"id\": \"asset-management\"},
  \"data\": {
    \"action\": \"REPORT_SERVICE_USAGE_TO_ODOO\",
    \"usage_type\": \"battery_swap_completed\",
    \"service_completion_details\": {
      \"old_battery_id\": \"BAT_001\",
      \"new_battery_id\": \"BAT_002\",
      \"energy_transferred\": 45.5,
      \"service_duration\": 180
    }
  }
}"
sleep 3

# Step 4: Wait for Odoo billing echo
echo "Step 4: Waiting for Odoo billing confirmation..."
echo "(Odoo will send echo message with invoice details)"
sleep 2

echo ""
echo "✅ Odoo sync workflow completed!"
echo "Subscription synced → Service used → Billing reported → Invoice generated"

Monitoring Odoo Integration

Terminal 1: Subscription sync messages

emit/odo/subscription/#
echo/odo/subscription/#

Terminal 2: Billing messages

emit/uxi/billing/#
echo/uxi/billing/#
emit/abs/billing/#
echo/odo/billing/#

Terminal 3: All Odoo messages

emit/odo/#
echo/odo/#


Integration Points

Odoo → ABS (Subscription Sync)

When: Triggered by Odoo ERP on: - New subscription created - Payment status changed - Subscription renewed - Subscription cancelled

Topic: emit/odo/subscription/plan/{plan_id}/sync


ABS → Odoo (Usage Reporting)

When: Triggered by ABS after: - Battery swap completed - Service milestone reached - Quota exhausted (invoice generation)

Topic: emit/uxi/billing/plan/{plan_id}/usage_report
Secondary: emit/abs/billing/plan/{plan_id}/swap_completed


Odoo → ABS (Billing Confirmation)

When: Triggered by Odoo after: - Invoice generated - Payment processed - Billing milestone confirmed

Topic: echo/odo/billing/plan/{plan_id}/billing_processed


Common Errors

Error Signal Cause Solution
ODOO_SUBSCRIPTION_ID_MISSING Missing subscription ID Provide valid Odoo subscription ID
PAYMENT_STATE_INVALID Invalid payment state Use valid Odoo payment state
SUBSCRIPTION_STATE_INVALID Invalid subscription state Use valid Odoo subscription state
SYNC_FAILED Odoo connection error Check Odoo ERP connectivity

Next Steps