Integration Architecture and Data Flow
A successful Wave to Odoo 18 integration hinges on a well-defined architectural pattern. The design must account for the distinct data models and authentication methods of each system. Wave operates with a REST API, while Odoo 18 uses both its XML-RPC and JSON-RPC interfaces. The core architecture employs a middleware layer, often a custom Odoo module or a dedicated service, that acts as the orchestration engine. This component manages the bidirectional data flow, handles authentication, and executes the transformation logic between the two platforms.
Core Synchronization Patterns
The integration uses two primary data synchronization patterns: event-driven and scheduled. Event-driven synchronization relies on webhooks. Wave can send instant notifications for new transactions or updated invoices. Odoo 18 listens for these webhook calls and processes the data immediately. This pattern ensures near real-time data consistency for critical financial events. It provides the fastest possible update cycle for high-priority records like customer payments and supplier bills.
Scheduled synchronization handles bulk data and complex master records. A cron job within Odoo 18 runs at set intervals, perhaps every hour or twice daily. This job polls the Wave API for new or modified records that webhooks might miss. It processes larger datasets, such as historical transactions or product catalog updates. This batch approach manages system load and handles data that does not require instant updates. You use it for initial data imports and non-critical record types.
Data Flow for Key Objects
The flow for a customer payment demonstrates the entire process. A payment records in Wave. The Wave webhook sends a POST request to a predefined endpoint in your Odoo module. The Odoo endpoint authenticates the request using a pre-shared secret. It then calls the Wave API to fetch the complete payment details using the ID from the webhook payload. The Odoo module maps the Wave payment fields to an Odoo account.move record with a type of ‘entry’. It creates the journal entry, posts it, and reconciles it against the corresponding invoice. The system logs the Wave transaction ID in a custom Odoo field to prevent duplicate processing.
Step-by-Step Configuration
Begin the configuration by establishing API access in both systems. In Wave, navigate to the Settings section and access the “Integrations” area. Create a new application to receive your Client ID and Client Secret. These credentials authenticate your Odoo instance with the Wave API. In Odoo 18, you must enable developer mode. Navigate to Settings > General Settings and activate the developer options. This step grants you access to the underlying App creation and module development tools.
Building the Odoo Integration Module
Create a new custom Odoo module to house the integration logic. Define your module’s manifest file with the necessary dependencies. You must declare dependencies on the account, sale, and purchase Odoo applications. Your __manifest__.py file should resemble this code block.
{
'name': 'Wave Accounting Integration',
'version': '18.0.1.0.0',
'category': 'Accounting',
'summary': 'Bidirectional sync with Wave',
'depends': ['account', 'sale', 'purchase'],
'data': [
'security/ir.model.access.csv',
'views/res_config_settings_views.xml',
'views/account_move_views.xml',
'data/ir_cron_data.xml',
],
'installable': True,
'application': True,
}
Create the configuration model to store Wave API credentials securely. Use Odoo’s res.config.settings model to add fields for the Client ID and Client Secret. This method stores sensitive data encrypted within the Odoo database. It also provides a user-friendly interface for system administrators to enter the connection details without touching code.
Implementing the Authentication Handler
Your module needs a dedicated class to manage Wave API authentication. The Wave API uses the OAuth 2.0 protocol. Your authentication handler must obtain and refresh access tokens. Implement a method that requests a new token using the client credentials grant. Store the token and its expiration timestamp in Odoo’s key-value store, ir.config_parameter. This storage mechanism keeps the token persistent across server restarts.
import requests
from odoo import models, fields, api
class WaveAPI(models.AbstractModel):
_name = 'wave.api'
def _get_access_token(self):
base_url = "https://api.waveapps.com"
client_id = self.env['ir.config_parameter'].get_param('wave.client_id')
client_secret = self.env['ir.config_parameter'].get_param('wave.client_secret')
token_url = f"{base_url}/oauth2/token/"
data = {
'grant_type': 'client_credentials',
'client_id': client_id,
'client_secret': client_secret,
}
response = requests.post(token_url, data=data)
response.raise_for_status()
token_data = response.json()
self.env['ir.config_parameter'].set_param('wave.access_token', token_data['access_token'])
return token_data['access_token']
Configuring Webhook Endpoints in Odoo
Define controller endpoints in your Odoo module to receive Wave webhooks. Create a /wave/webhook route that accepts POST requests. This controller must verify the webhook signature to ensure request authenticity. It should then delegate the processing to a background job. Using a queue job prevents the webhook from timing out during complex data operations. This design maintains a fast response time for Wave.
from odoo import http
from odoo.http import request
import json
import hmac
import hashlib
class WaveWebhook(http.Controller):
@http.route('/wave/webhook', type='json', auth='public', methods=['POST'], csrf=False)
def wave_webhook(self):
webhook_secret = request.env['ir.config_parameter'].get_param('wave.webhook_secret')
signature = request.httprequest.headers.get('X-Wave-Signature')
body = request.httprequest.get_data()
computed_signature = hmac.new(webhook_secret.encode(), body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(signature, computed_signature):
raise ValueError("Invalid webhook signature")
data = json.loads(request.httprequest.data)
request.env['wave.sync.job'].with_delay().process_webhook(data)
return {'status': 'accepted'}
Setting Up Synchronization Cron Jobs
Configure scheduled actions for batch synchronization. Create XML records for ir.cron that call your synchronization methods. Set one cron job to import new Wave transactions every hour. Configure another job to export Odoo invoices to Wave on a daily schedule. This scheduled sync acts as a safety net for any records that miss webhook notifications.
<record id="cron_import_wave_transactions" model="ir.cron">
<field name="name">Wave: Import Transactions</field>
<field name="model_id" ref="model_wave_sync_job"/>
<field name="state">code</field>
<field name="code">model._import_wave_transactions()</field>
<field name="interval_number">1</field>
<field name="interval_type">hours</field>
<field name="numbercall">-1</field>
</record>
Data Mapping and Transformation
The core challenge of this integration lies in the semantic mapping between Wave’s accounting model and Odoo’s operational model. Wave centers on the Transaction as its primary object. Odoo 18 uses the Account Move as the core document for all journal entries. A single Wave transaction, which can represent a bank deposit or expense, often maps to a complete account move in Odoo with multiple line items. You must design a transformation engine that deconstructs the Wave object and rebuilds it according to Odoo’s accounting schema.
Chart of Accounts Alignment
Before any transaction sync, you must achieve a baseline alignment between the charts of accounts. Wave and Odoo 18 use different default account structures. Create a mapping table that correlates Wave account IDs to Odoo account IDs. This table resides in a new Odoo model, wave.account.map. Store the Wave account id and name alongside the corresponding Odoo account.account ID. The synchronization logic references this mapping table for every transaction line it processes.
For accounts that lack a direct counterpart, implement a fallback strategy. The system can create new accounts in Odoo on the fly during the initial sync, or it can route unmapped transactions to a suspense account for manual review. We recommend the manual review approach for production environments because it prevents misclassified data. The system flags unmapped transactions in an error log for later resolution by an accountant.
Customer and Vendor Identity Resolution
Linking Wave transactions to Odoo partners requires a robust identity resolution process. Wave stores customer and vendor names, while Odoo uses a centralized res.partner model. The integration must match incoming Wave transaction names to existing Odoo partners. Implement a matching algorithm that uses a combination of exact name lookup and fuzzy matching. Store the successful Wave customer ID to Odoo partner ID matches in a dedicated mapping table, wave.partner.map, to accelerate future syncs.
When the system finds no match for a Wave customer, it must decide whether to create a new partner in Odoo automatically. For sales transactions, creating a new partner often makes sense. For expense transactions linked to a new vendor, you might prefer a manual review process. Add a configuration setting in Odoo that lets administrators choose the creation policy for unmatched customers and vendors. This control prevents partner duplication and maintains data quality.
Invoice and Payment Lifecycle Mapping
The invoice and payment synchronization requires careful state management. An invoice in Wave can have states like DRAFT, OVERDUE, or PAID. Odoo invoices have states including draft, posted, and paid. The integration must translate these states and also handle the complete payment allocation. When a Wave transaction pays an invoice, the integration must find the corresponding Odoo invoice, create the payment, and reconcile them.
This process uses the external identifiers from both systems. The integration stores the Wave invoice ID in a custom field on the Odoo account.move record. Conversely, it stores the Odoo invoice ID in the Wave invoice as a custom attribute via the Wave API. This bidirectional linking enables the payment reconciliation logic to work reliably. The system queries Wave for payments linked to a specific invoice ID, then finds the matching Odoo invoice using the stored Wave ID, and finally creates the reconciliation in Odoo.
Error Handling and Resilience
The integration will encounter errors. Your design must anticipate common failure points and handle them with specific recovery procedures. Network timeouts when calling the Wave API represent a frequent issue. Implement a retry mechanism with exponential backoff for all external API calls. The system should log each retry attempt and send a notification to administrators after a defined number of failures. This pattern prevents temporary network glitches from causing data loss.
Handling API Rate Limits
The Wave API enforces strict rate limits. Exceeding these limits results in HTTP 429 errors. Your integration code must catch these responses and pause subsequent requests. Track the X-RateLimit-Remaining header in every API response. When the remaining requests drop below a safe threshold, the system should queue new requests and execute them after the rate limit window resets. This proactive approach avoids many rate limit errors.
For scheduled sync jobs, implement a leaky bucket algorithm. This algorithm controls the flow of API requests to maintain a steady, permissible rate. It spreads the requests evenly over the sync window instead of sending them in bursts. This method maximizes throughput without triggering rate limits. It provides a more reliable sync experience than simple retry logic alone.
Data Validation and Conflict Resolution
Data conflicts occur when the same record updates in both systems between sync cycles. The integration needs a conflict resolution strategy. We recommend a configurable policy, such as “Wave wins” for financial data or “Odoo wins” for operational data. When the system detects a conflict, it should log the discrepancy and follow the configured policy. It can also flag the record for manual review if the conflict affects a critical accounting field.
Validate all data before writing it to either system. Check for required fields, data formats, and logical consistency. For example, an invoice line must have both an account and an amount. If the Wave transaction lacks a required field, the integration should not create a corrupt record in Odoo. Instead, it places the transaction in a quarantine area and notifies an administrator. This validation prevents the propagation of bad data.
Transaction Integrity and Rollback
Maintain data integrity by implementing atomic operations where possible. When creating an invoice in Odoo with multiple lines, wrap the entire process in a database transaction. If any line fails, the entire transaction rolls back. This prevents the creation of partial or unbalanced journal entries. For complex multi-system updates, use compensation actions, also known as sagas, to undo completed steps if a later step fails.
For example, if the integration creates an invoice in Odoo but then fails to mark it as sent in Wave, it should not leave the Odoo invoice in a posted state. The system should either retry the Wave update or, if repeated failures occur, reverse the Odoo invoice. This compensation logic ensures the two systems remain consistent even after a partial failure. It requires careful design but provides robust fault tolerance.
Testing and Validation
A rigorous testing protocol separates a production-ready integration from a fragile prototype. Begin with unit tests for your data transformation functions. Verify that your mapping logic converts Wave transaction data into the correct Odoo journal entry structure. Mock the API calls to isolate the transformation logic from external dependencies. These tests run fast and catch mapping errors early in the development cycle.
Integration Test Scenarios
Create a comprehensive suite of integration tests that exercise the complete data flow. Use a dedicated sandbox environment for both Wave and Odoo. These tests should cover all major business scenarios: creating a customer invoice in Odoo and syncing it to Wave, recording a payment in Wave and reconciling it in Odoo, and entering an expense in Wave and creating a vendor bill in Odoo. Each test must validate the data in both systems after the sync completes.
Prepare a set of test data that mirrors your live business operations. Include various transaction types: sales invoices with different tax rates, partial customer payments, supplier bills with early payment discounts, and bank transfer fees. This diverse dataset exposes edge cases that simpler tests might miss. Execute the full test suite after every code change to prevent regressions.
End-to-End User Acceptance Testing
Before going live, conduct User Acceptance Testing with the actual business users. The finance team should verify that the synced data appears correctly in financial reports. The sales team should confirm that paid invoices update the customer status in Odoo. Create a checklist of critical business processes and have users sign off on each one. This validation ensures the integration meets real business needs, not just technical specifications.
Test the system under load to gauge its performance. Import a large batch of historical transactions to see how the integration handles volume. Monitor system resources in Odoo during the sync. Identify any bottlenecks, such as slow API endpoints or database locks. Performance testing reveals scalability issues that could impact the system during peak accounting periods, like month-end close.
Validation Checklists and Metrics
Develop a go-live validation checklist. This list includes items like “Verify OAuth connection to Wave,” “Confirm webhook endpoint receives calls,” and “Check that error notifications route to the correct team.” Run through this checklist after deploying the integration to a staging environment and again after the production deployment. This procedural rigor prevents oversights.
Define clear success metrics for the integration. Track the number of transactions processed per day, the average sync latency, and the error rate. Monitor these metrics during the initial weeks after deployment. A stable integration should show a high transaction volume with a low and stable error rate. These metrics provide objective evidence that the integration operates as intended and delivers business value.
Security Considerations
The integration handles sensitive financial data, so security requires paramount attention. All communication between Odoo and Wave must use TLS encryption. Verify that your Odoo server enforces HTTPS and uses current, secure cipher suites. The Wave API mandates HTTPS, but you must ensure your Odoo instance meets the same standard. Encrypt data in transit to prevent eavesdropping on financial information.
Credential Management Best Practices
Store all API credentials, especially the Wave Client Secret, using Odoo’s built-in encryption mechanisms. The ir.config_parameter model can store parameters as password fields, which obscures their values in the database and the user interface. Never hardcode credentials in your module’s Python files or XML data. Restrict access to the integration configuration settings to authorized administrators only.
Implement the principle of least privilege for the Wave API connection. The integration only needs specific scopes to function. Request only the permissions you absolutely need, such as transaction:read and invoice:write. Avoid overly broad scopes like account:read unless every permission within that scope serves a defined purpose. Limited scopes reduce the potential damage from a credential leak.
Webhook Security and Validation
Secure your webhook endpoint against unauthorized calls. The Wave webhook includes a signature header, X-Wave-Signature, which is a hash of the payload using your webhook secret. Your endpoint must compute the same hash and verify the signatures match before processing any data. Reject any request with an invalid or missing signature. This validation prevents attackers from injecting fake data into your Odoo system.
Regularly rotate your webhook secret and API credentials. Establish a procedure to update these secrets every 90 days. This practice limits the window of opportunity if credentials ever leak. Design the integration to support credential rotation without downtime. You should update the secrets in the Odoo configuration settings without needing to restart the Odoo server or pause synchronization jobs.
Data Audit and Compliance
Maintain a detailed audit log of all integration activities. Record every data synchronization event, including the source record, the action taken, and any errors encountered. This log assists with troubleshooting and demonstrates compliance with financial controls. In the event of a data discrepancy, the audit log provides a complete trail for forensic analysis.
Consider data residency and privacy regulations. If your business operates in regions with strict data protection laws, understand where Wave processes and stores your data. The integration transfers financial data between these two systems, so you must ensure this cross-border data flow complies with all applicable regulations like GDPR or CCPA. Document the data flow as part of your organization’s privacy policy.
Performance Optimization
The integration’s performance directly impacts user experience and data freshness. Identify and address common bottlenecks to ensure a responsive system. The most frequent performance issue involves excessive API calls to Wave. Each call has latency, and making calls in a sequential loop for hundreds of transactions creates significant delay. Minimize API calls by fetching data in batches where the API supports it.
Efficient Data Polling Strategies
Optimize your scheduled synchronization jobs. Instead of querying Wave for all transactions every time, use incremental polling. Store the timestamp of the last successful sync. On subsequent runs, query only for transactions modified since that timestamp. This technique reduces the data volume each job must process and shortens the sync cycle. It puts less load on both the Wave API and your Odoo database.
For the initial data import, performance is critical. Importing years of transaction history can take many hours. Break the initial import into chunks by date range. Process one month or one quarter of data at a time. This approach prevents the job from timing out and makes progress easier to monitor. It also reduces the memory footprint of the import process inside Odoo.
Caching and Database Indexing
Implement a caching layer for static reference data. The mapping between Wave accounts and Odoo accounts rarely changes. Load this mapping into memory at the start of a sync job instead of querying the database for every transaction line. This simple cache can cut database query volume by half for a typical transaction sync. Clear the cache when administrators update the account mapping.
Add database indexes to custom fields used for synchronization. The field that stores the Wave transaction ID on the Odoo account.move record must have a database index. Without an index, the query to check for existing transactions performs a full table scan, which becomes slow as you accumulate thousands of records. Proper indexing keeps lookup times fast regardless of data volume.
Background Job Processing and Queue Management
Delegate all heavy synchronization work to background jobs. Odoo’s built-in queue job system processes tasks asynchronously. This design keeps the web interface responsive. When a webhook arrives, the controller quickly validates it and enqueues a job. The job then runs in the background, processing the data without blocking other operations. Configure enough queue workers to handle your expected sync load.
Monitor the queue length and processing times. A growing queue indicates the system cannot keep up with the incoming data volume. If you observe consistent queue growth, scale up by adding more Odoo workers or optimize the slowest jobs. Use Odoo’s built-in monitoring or external tools like Prometheus to track key performance indicators. Proactive monitoring helps you address performance issues before they impact the business.