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¶
- Attendant Workflow Guide - Staff-operated swaps (requires Phase 0)
- Rider Workflow Guide - Mobile app-driven swaps (requires Phase 0)
- Service State Management - Quota tracking