Xero Integration Module¶
Synchronize invoices with Xero accounting software.
| Field | Value |
|---|---|
| Technical Name | xero_integration |
| Version | 15.0.1.0.0 |
| Category | Accounting |
| Dependencies | account, sale |
| Python Dependencies | xero_python, psycopg2 |
Overview¶
This module provides:
- OAuth 2.0 connection to Xero
- Automatic invoice sync when posted (non-blocking)
- Contact auto-creation/matching in Xero
- Payment sync via webhook + cron polling
- Support for deposit, final, and regular invoices
- Multi-company support
- Sync logging for troubleshooting
Invoice Types¶
Deposit Invoice¶
Detected when down payment line has positive quantity (+1.00):
Odoo: Xero (Account 835):
------------------------ ------------------------
Downpayment (50%) $2,700 -> "Deposit for SO-001"
Qty: 1 | Amount: $2,700
Final Invoice¶
Detected when down payment line has negative quantity (-1.00):
Odoo: Xero (2 lines):
------------------------ ------------------------
Products $5,400 Line 1: "SO-001 - Product - Customer" (400)
Amount: $5,400
Downpayment -$2,700 Line 2: "Less: Deposit Paid" (835)
Amount: -$2,700
------------------------ ------------------------
Total Due: $2,700 Total Due: $2,700
Regular Invoice¶
No down payment line present:
Odoo: Xero (Account 400):
------------------------ ------------------------
Products $5,400 -> "SO-001 - Product - Customer"
Qty: 1 | Amount: $5,400
Xero Accounts¶
| Code | Name | Used For |
|---|---|---|
| 400 | Sales (default) | Regular and final invoice project totals |
| 835 | Customer Deposits (default) | Deposit invoice + deposit deduction |
Account codes are configurable in the Xero connection settings.
Data Models¶
xero.account¶
Stores Xero OAuth connection and configuration.
| Field | Type | Description |
|---|---|---|
name |
Char | Connection name |
company_id |
Many2one | Link to res.company |
client_id |
Char | Xero app client ID |
client_secret |
Char | Xero app client secret |
redirect_uri |
Char | OAuth redirect URI |
webhook_key |
Char | Webhook signature verification key |
access_token |
Text | Current access token |
refresh_token |
Text | Refresh token |
token_expires |
Datetime | Token expiration time |
tenant_id |
Char | Xero organization ID |
tenant_name |
Char | Xero organization name |
downpayment_product_id |
Many2one | Down payment product for detection |
payment_journal_id |
Many2one | Journal for incoming payments |
sales_account_code |
Char | Xero sales account (default: 400) |
deposit_account_code |
Char | Xero deposit account (default: 835) |
connection_status |
Selection | disconnected / connected / error |
last_sync |
Datetime | Last invoice sync |
last_payment_sync |
Datetime | Last payment sync |
xero.sync.log¶
Logs all sync operations for troubleshooting.
| Field | Type | Description |
|---|---|---|
name |
Char | Log entry name |
model |
Char | Related model (account.move, etc.) |
res_id |
Integer | Related record ID |
direction |
Selection | inbound / outbound |
status |
Selection | success / error |
error_message |
Text | Error details |
response_data |
Text | API response data (JSON) |
Extended Fields¶
res.partner:
| Field | Type | Description |
|---|---|---|
xero_contact_id |
Char | Xero contact GUID |
xero_sync_date |
Datetime | Last sync timestamp |
account.move:
| Field | Type | Description |
|---|---|---|
xero_invoice_id |
Char | Xero invoice GUID |
xero_sync_status |
Selection | pending / synced / error |
xero_sync_date |
Datetime | Last sync timestamp |
xero_sync_error |
Text | Error message |
Configuration¶
1. Xero Developer Portal¶
- Create app at https://developer.xero.com
- App type: Web app
- Set redirect URI:
https://your-odoo-domain.com/xero/callback - Copy Client ID and Client Secret
- For webhooks, note your Webhook Key
2. Odoo Setup¶
Navigate to Accounting > Configuration > Xero Connections:
- Click Create
- Fill in connection details:
| Field | Description | Example |
|---|---|---|
| Connection Name | Display name | "Production Xero" |
| Company | Odoo company | Your Company |
| Client ID | From Xero portal | ABC123... |
| Client Secret | From Xero portal | secret... |
| Redirect URI | OAuth callback | https://odoo.example.com/xero/callback |
| Webhook Key | For signature verification | webhook-key... |
| Down Payment Product | Product for DP detection | "Down Payment" |
| Payment Journal | Bank/cash journal | "Bank" |
| Sales Account Code | Xero sales account | "400" |
| Deposit Account Code | Xero deposit account | "835" |
- Click Connect to Xero button
- Authorize access in Xero popup
- Verify connection shows Connected
3. Python Dependencies¶
Contact Matching¶
When syncing an invoice, contacts are matched in this order:
- Use existing
xero_contact_idif already linked - Search Xero by email (exact match)
- Search Xero by name (exact match)
- Create new contact in Xero
Invoice Sync¶
Automatic Sync¶
Invoices are automatically synced to Xero when posted:
- Sync is non-blocking - invoice posts even if Xero sync fails
- Errors are logged and can be retried manually
- Only customer invoices (
out_invoice) and credit notes (out_refund) are synced
Manual Sync¶
For failed syncs, use the Sync to Xero button on the invoice form.
Invoice Type Detection¶
The module detects invoice type by checking for down payment lines:
# Down payment detection (in order):
1. Product ID matches configured downpayment_product_id
2. Product name contains: "down payment", "downpayment", "deposit", "advance"
# Type determination:
- No DP line -> Regular invoice
- DP qty > 0 -> Deposit invoice
- DP qty < 0 -> Final invoice (with deposit deduction)
Payment Sync¶
Webhook (Real-time)¶
Configure webhook in Xero developer portal:
- URL:
https://your-odoo-domain.com/xero/webhook - Events: Invoice payments
Cron Polling (Backup)¶
Scheduled job runs every 15 minutes:
- Queries Xero for invoices with status "PAID"
- Creates payment records in Odoo
- Reconciles with invoice
Payment processing is idempotent - safe to process the same payment multiple times.
Module Structure¶
xero_integration/
├── __manifest__.py
├── __init__.py
├── webhook_middleware.py # WSGI middleware for webhooks
├── controllers/
│ ├── __init__.py
│ ├── oauth.py # OAuth callback handler
│ └── webhook.py # Payment webhook handler
├── models/
│ ├── __init__.py
│ ├── xero_account.py # OAuth connection & config
│ ├── xero_sync_log.py # Sync logging
│ ├── account_move.py # Invoice extension
│ └── res_partner.py # Contact extension
├── views/
│ ├── oauth_templates.xml # OAuth success/error pages
│ ├── xero_account_views.xml
│ ├── xero_sync_log_views.xml
│ ├── account_move_views.xml
│ └── res_partner_views.xml
├── security/
│ └── ir.model.access.csv
└── data/
└── ir_cron.xml # Payment sync cron job
OAuth Scopes¶
Required Xero OAuth scopes:
| Scope | Purpose |
|---|---|
offline_access |
Refresh token support |
accounting.transactions |
Create/read invoices |
accounting.contacts |
Create/read contacts |
accounting.settings |
Read account settings |
Development Setup¶
For local development with ngrok:
# Start Odoo
docker compose up -d
# Start ngrok tunnel
ngrok http 8015
# Copy ngrok URL (e.g., https://abc123.ngrok.io)
# Update in Xero Developer Portal:
# - Redirect URI: https://abc123.ngrok.io/xero/callback
# - Webhook URL: https://abc123.ngrok.io/xero/webhook
Troubleshooting¶
Connection Failed¶
- Verify Client ID and Secret are correct
- Check redirect URI matches exactly (including https)
- Ensure Odoo is accessible from internet for OAuth callback
Invoice Sync Failed¶
- Check sync log: Accounting > Configuration > Xero Sync Log
- Verify Xero connection is active
- Use Sync to Xero button to retry
- Check Odoo logs for detailed error
Payment Not Syncing¶
- Verify webhook is configured in Xero
- Check payment journal is configured
- Run manual sync: Accounting > Configuration > Xero Connections > Poll Payments
- Check cron job is running (every 15 minutes)
Token Expired¶
Token refresh is automatic. If issues persist:
- Click Disconnect in Xero connection
- Click Connect to Xero to re-authorize
API Reference¶
Xero API Endpoints¶
| Endpoint | Method | Purpose |
|---|---|---|
login.xero.com/identity/connect/authorize |
GET | OAuth authorization |
identity.xero.com/connect/token |
POST | Token exchange/refresh |
api.xero.com/connections |
GET | Get tenant info |
api.xero.com/api.xro/2.0/Invoices |
POST | Create invoice |
api.xero.com/api.xro/2.0/Contacts |
GET/POST | Contact operations |