1. Introduction
Odoo est aujourd’hui l’un des ERP les plus répandus au Maroc, grâce à son architecture modulaire, son code source public et sa capacité à répondre aux besoins des petites, moyennes et grandes entreprises.
Dans une économie où les services en ligne (paiement mobile, facturation électronique, services publics, etc.) se multiplient, il devient essentiel de faire parler Odoo à d’autres systèmes. Que vous souhaitiez récupérer des données de caisse, pousser des factures vers un service de signature électronique ou simplement mettre à jour votre stock à partir d’un fournisseur externe, la plupart de ces cas d’usage se résolvent par l’intégration d’API tierces.
Cet article se concentre sur les points clés pour connecter Odoo (version 16 ou 17) à des API locales et internationales tout en respectant les contraintes spécifiques du Maroc : localisation des données, conformité RGPD, accès à des services de paiement marocains, etc.
2. Pourquoi connecter Odoo à des API tierces ?
| Utilité | Exemple | Impact sur l’entreprise |
|---|---|---|
| Automatisation des flux | Obtenir le taux de change des banques en temps réel | Réduction du travail manuel et diminution des erreurs |
| Points de vente numériques | Intégrer M-Pesa/mobile‑money capable | Générer des ventes 24 / 7 sans passer par un guichet bancaire |
| Communication achat/vente | Synchroniser les stocks d’un fournisseur via son API | Eviter les ruptures de stock et sur‑estimer les quantités |
| Réglementation | Soumettre des factures électroniques au Office National des Impôts (ONI) | Se conformer aux obligations fiscales marocaines |
| Analyse de données | Récupérer les données CRM d’un partenaire | Améliorer la prise de décision grâce à un tableau de bord fusionné |
3. Étapes générales d’une intégration Odoo / API tierce
3.1. Analyse & cahier des charges
- Choix de l’API : inspectez la documentation (Swagger, Postman, docs GitHub).
- Cas d’usage : définissez clairement la ou les fonctionnalités qui nécessitent l’appel d’API.
- Fréquence : en temps réel, periodique (cron), événementiel (webhook).
- Sécurité : OAuth 2.0? API‑token? Certificat client?
3.2. Environnement de développement
- Clonez votre instance Odoo localement ou utilisez un environnement Docker.
- Créez un module de démonstration pour ne pas toucher aux modules principaux.
- Installez
requestsou un autre client HTTP (aiohttpsi vous jouez avec l’asynchrone).
3.3. Architecture technique
- Modèle métier (
ir.model): stocker les tokens, les URLs, les paramètres. - Programmeur python: la logique d’appel API, le parsing, la création de champs virtuels si besoin.
- Cron Jobs (
ir.cron): appels periodiques. - Webhooks (
odoo.http:@request), ou écoute d’événements de backend. - Gestion des erreurs: logs, notifications mails, alertes Slack, etc.
- Sécurité: chiffrement dans la base (fields
password+_auto_init_db) ou via le fichierodoo.conf.
3.4. Implémentation scroll
3.4.1. Modèle de configuration
# file: addons/odoo_api_tiers/models/api_tiers.py
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
import requests
class ApiTiers(models.Model):
_name = 'api.tiers'
_description = 'Configuration des API tierces'
_rec_name = 'name'
name = fields.Char('Nom de l’API', required=True, translate=True)
base_url = fields.Char('URL de base', required=True)
api_key = fields.Char('Clé API', required=True, help="Token ou clé d’authentification")
test_url = fields.Char('URL test', help="Endpoint de test pour valider la connexion")
is_active = fields.Boolean('Actif', default=True)
3.4.2. Méthodes d’appel
class ApiTiers(models.Model):
_inherit = 'api.tiers'
@api.model
def _call_api(self, endpoint, method='GET', data=None, headers=None, params=None):
config = self.sudo().search([('is_active', '=', True)], limit=1)
config:
raise ValidationError(_('Aucune configuration API active.'))
url = f"{config.base_url.rstrip('/')}/{endpoint.lstrip('/')}"
h = headers or {}
h.setdefault('Authorization', f"Bearer {config.api_key}")
h.setdefault('Content-Type', 'application/json')
session = requests.Session()
response = session.request(method, url, headers=h, json=data, params=params, timeout=30)
if not response.ok:
_logger.error('API %s returned error: %s', config.name, response.text)
raise ValidationError(_('Erreur dans la requête API : %s', response.text))
return response.json()
3.4.3. Exemple d’utilisation : synchronisation de stocks
@api.model
def sync_stock_from_provider(self):
"""Appel à l’API fournisseur (ex : `GET /products`)"""
data = self._call_api('/products')
for prod in data:
vals = {
'name': prod['name'],
'default_code': prod['sku'],
'qty_available': prod['stock'],
}
# Ici on crée ou met à jour en mode “create” seulement si stock > 0
if prod['stock'] > 0:
product_obj = self.env['product.product']
product = product_obj.search([('default_code', '=', prod['sku'])], limit=1)
if product:
product.write(vals)
else:
product_obj.create(vals)
3.4.4. Crontab
# file: addons/odoo_api_tiers/odoo_api_tiers.xml
<odoo>
<data noupdate="1">
<record id="ir_cron_sync_stock" model="ir.cron">
<field name="name">Synchronisation stocks fournisseur</field>
<field name="model_id" ref="model_api_tiers"/>
<field name="state">code</field>
<field name="code">model.sync_stock_from_provider()</field>
<field name="interval_number">6</field>
<field name="interval_type">hours</field>
<field name="doall" eval="False"/>
</record>
</data>
</odoo>
4. Sélection de l’API tierce : contraintes locales
4.1. Services publics
| API | Description | Spécificité |
|---|---|---|
| ONI (Office National des Impôts) | Facturation électronique et déclaration des ventes | Données sensibles / chiffrement obligatoire, ISO/IEC 27001 |
| API de la Banque Centrale | Taux de change, horaires d’ouverture | Accès restreint, cert. client obligatoire |
| API de la Société Nationale des Postes (ONPT) | Suivi colis | requiert un abonnement et token d’accès |
4.2. Paiements mobiles & bancaires
- MoMo/Orange Money : OAuth2.0 avec code d’autorisation, temps de connexion court (token 1h).
- ATB e‑payment : API REST protégée par clé & certificat RSA.
- Maroc Telecom : Solde & recharge via API Paylink.
4.3. Cartographie des exigences
| Critère | Exigences |
|---|---|
| Authentification | OAuth 2.0 / API clé / JWT |
| Sécurité | TLS 1.2, chiffrement des champs sensibles |
| Localisation des données | Envoi de données au doyen agreed ou stockage dans un datacenter marocain (Règlement RGPD) |
| Latence | Optimiser les appels bcp utilisés, enregistrer en cache. |
| Compatibilité rev | API REST (JSON) standard, pour Odoo 16/17 il faut bien gérer la version d’XML‑RPC si besoin. |
5. Exemple concret : Intégration du paiement MoMo (Orange Money)
5.1. Processus en 5 étapes
-
Obtention d’un code client
- Vous devez demander un « client ID » à Orange Money via leur portail développeur. Ce token est valide pour 6 mois.
-
Authentification
- Post
/oauth/tokenavecgrant_type=client_credentials. - Vous recevez un
access_tokenà inclure dans l’en-têteAuthorization: Bearer {token}.
- Post
-
Création d’une transaction
- Endpoint
POST /api/transactions. - Corps JSON : montant, type et référence.
- Endpoint
-
Suivi
GET /api/transactions/{ref}ou webhookPOST /api/transactions/webhook.
- Réconciliation
- Enregistrer
transaction_iddans un champ virtuelmo_transactiondu Moduleaccount.move. - Mettre à jour le statut de la facture (
paid).
- Enregistrer
5.2. Code d’appel complet
class MoMoWizard(models.TransientModel):
_name = 'wizard.momo'
_description = 'Wizard de paiement MoMo'
amount = fields.Float('Montant', required=True)
invoice_id = fields.Many2one('account.move', string='Facture')
@api.model
def create_transaction(self):
# Auth
token_res = requests.post(
'https://api.momo.orange.com/oauth/token',
data={'grant_type': 'client_credentials'},
auth=('client_id', 'client_secret')
).json()
access_token = token_res['access_token']
# Transaction
tx_payload = {
"amount": self.amount,
"currency": "MAD",
"reference": f"I{self.invoice_id.id}",
"description": f"Paiement facture {self.invoice_id.name}"
}
tx_res = requests.post(
'https://api.momo.orange.com/api/transactions',
json=tx_payload,
headers={'Authorization': f'Bearer {access_token}'}
).json()
# Relier à la facture
self.invoice_id.write({
'mo_transaction': tx_res['transaction_id'],
'mo_status': tx_res['status']
})
return True
5.3. Gestion des webhooks
from odoo import http
from odoo.http import request
class MoMoWebhook(http.Controller):
@http.route('/momo/webhook', type='json', auth='none', csrf=False)
def momo_webhook(self, **payload):
# payload = {"transaction_id": "...", "status" :"SUCCESS"}
inv = request.env['account.move'].sudo().search(
[('mo_transaction', '=', payload['transaction_id'])],
limit=1
)
if inv:
inv.message_post(body=f"Webhook MoMo: {payload['status']}")
inv.mo_status = payload['status']
return {'status': 'ok'}
6. Sécurité & conformité pour le Maroc
| Objectif | Pratique recommandée |
|---|---|
| RGPD | Gérer la base de données avec la fonction decrypt dans Odoo, conserver les données relatives aux usagers avec leur consentement. |
| Sécurisation API | Chiffrer les tokens dans la base (field = fields.Char('Token', help="...", store=False) + store=True). |
| Backup | Utiliser Snapshots Azure ou Google Cloud, localiser dans le Maroc. |
| Audit | Scripter des logs dans Odoo (_logger.info). |
| Interopérabilité | Utiliser des formats ISO 20022 pour les données de paiement, afin de faciliter les échanges avec les banques locales. |
7. Responsabilité juridique
- Licence Odoo : Vous pouvez développer des modules personnalisés tant qu’ils respectent la licence Odoo Community (AGPLv3).
- API données tierces : Veillez à respecter la politique de chaque API, notamment concernant l’usage commercial et les quotas.
- Odoo Production les usines majeures, la licence Enterprise propose des niveaux de support et garantit la conformité d’intégration.
8. Bonnes pratiques & astuces
- Utilisez le framework de test d’Odoo :
self.env['ir.http'].localhost. - Décompressez les
json: évitez de stocker les réponses brutes dans le champtext. - Cache : stockez les coupons, taux de change en cache Redis (Odoo 16 permet
ir.config_parameter). - Automate : Coupez le code d’intégration dans un module
cron(et n’ayez pas de « code dans le model ») pour éviter le “code spaghetti”. - Surveillance : Envisagez d’intégrer Sentry ou Grafana pour collecter les erreurs d’API.
- Gestion des régressions : Si l’API change de version, utilisez le principe “inversion de contrôle” avec un “tache de version” (
api.version). - Localisation : Pour les API marocaines, utilisez des noms de domaine .ma pour réduire la latence et respecter l’obligation d’hébergement national.
- Réplication : Si vous avez une instance Odoo répartie, centralisez l’authentification API via un Key‑Vault (Azure/Google Cloud) plutôt qu’en recopiant la clé sur chaque serveur.
9. Cas d’usage supplémentaires
| Domaine | API tierce | Intégration concrète |
|---|---|---|
| Énergie | API MARTEC (billetterie d’électricité) | Gérer automatiquement les factures de kWh dans Odoo |
| E‑commerce | DHL Maroc API | Suivi colis (tracking) directement dans le module sale.order |
| Retail | SAP SuccessFactors | Synchroniser les ventes en magasin en temps réel |
| Transport | Moovit Transit API | Lister les horaires de bus pour un service de logistique |
10. Conclusion
Connecter Odoo à des API tierces est une étape cruciale pour moderniser une entreprise marocaine : ça automatise, ça réduit les erreurs et ça donne accès à de nouvelles fonctionnalités. Le processus n’est pas compliqué mais il nécessite :
- Une bonne compréhension de votre flux métier – savoir quel est le gain réel.
- Un respect strict des exigences de sécurité et de conformité – particulièrement dans les secteurs sensibles.
- Une architecture décousue – séparer le code d’appel, les modèles, la logique métier et l’automatisation.
En suivant les étapes ci‑dessus, vous disposez d’un cadre solide pour débuter, que ce soit pour interagir avec un service public marocain ou un prestataire international. N’oubliez pas que l’intégration n’est qu’une partie du travail : tester, monitorer, surveiller et, surtout, garder votre code documenté.
Bonne mise en œuvre ! 🚀