Guide d'intégration ERP
Trois manières de relier Tauraco à un logiciel de gestion ou de comptabilité, de la plus simple à la plus intégrée :
- Fichiers : exports CSV, XLSX ou FEC, ponctuels ou planifiés (voir le guide des exports), déposés au besoin sur un serveur SFTP.
- API publique : votre logiciel lit et écrit dans Tauraco avec un jeton
client_credentials(exemple de synchronisation ci-dessous). - Connecteurs : Tauraco pousse les pièces vers votre logiciel, avec un mapping comptable déclaré et des rejets motivés.
État au 2026-11-05 : les connecteurs sont ouverts en production depuis le 2026-11-05 ; ils ont été développés et testés contre des serveurs simulés seulement, jamais contre l'environnement réel d'un éditeur. Règle de communication : tant qu'un connecteur n'a pas été construit et testé sur un environnement réel de l'éditeur, Tauraco annonce un « format compatible », jamais une « intégration native ».
Maturité des connecteurs
| Connecteur | Maturité prévue | Ce qu'il fait |
|---|---|---|
| CSV / XLSX générique | disponible | profils d'export (colonnes, séparateurs, formats de date) |
| REST générique | disponible | envoi JSON authentifié vers un endpoint que vous désignez, mêmes protections que les webhooks |
| SFTP sortant | disponible | dépôt de fichiers sur votre serveur ; empreinte d'hôte known_hosts obligatoire, hôte et répertoire fixés |
| Odoo 17 / 18 / 19 | bêta | test de connexion, envoi des partenaires et des factures en brouillon ; pas de tirage en v1 ; testé sur serveur simulé |
| Sage, QuickBooks, EBP, Pennylane (fichiers) | configuration requise | profils CSV au format attendu par le logiciel, sans API |
| SAP, Dynamics 365, Oracle ERP, PeopleSoft, NetSuite, Cegid, Xero, Sage X3, API Sage (Accounting, Intacct, Active) | non pris en charge | fiche documentée, aucun échange |
Le détail par éditeur (API, authentification, limites, points SYSCOHADA) figure dans le catalogue des connecteurs livré avec la documentation de la plateforme.
Mapping comptable
Tauraco ne reclasse jamais un compte : il applique la correspondance que vous déclarez (comptes, taxes, devises, journaux, tiers) et conserve la numérotation SYSCOHADA. Chaque pièce qui ne peut pas être envoyée est rejetée avec un motif, sans arrêter le lot (liste alignée le 2026-11-05 sur les motifs du moteur de mapping) :
| Motif | Sens |
|---|---|
missing_required | champ obligatoire absent |
unmapped_account | compte sans correspondance |
unmapped_tax | taxe sans correspondance |
currency_conversion_forbidden | devise différente sans conversion autorisée (jamais de conversion silencieuse) |
invalid_date | date illisible |
invalid_amount | montant invalide (non décimal, non fini ou plus précis que la devise) |
unknown_partner | tiers sans correspondance sur la connexion (à déclarer) |
duplicate | pièce déjà transmise à l'identique, ou présente deux fois dans le lot : elle est comptée skipped_duplicate (statut d'élément, pas un rejet) |
remote_rejected | refus du logiciel distant (message tronqué) |
invalid_value | valeur non convertible (par exemple un code devise qui n'a pas la forme ISO 4217 à trois lettres) |
Un lot avec des rejets se termine PARTIAL ; un rapport de réconciliation compare les totaux locaux et distants. Une erreur de transport arrête le lot (FAILED). Une simulation (dry_run) applique le mapping sans rien envoyer.
Synchroniser un logiciel comptable par l'API
L'exemple lit les factures modifiées depuis le dernier passage (updated_after, avec un recouvrement de 5 minutes), dédoublonne par identifiant et écrit un journal des ventes équilibré (client au débit, ventes et TVA au crédit). Les comptes sont ceux de votre plan comptable ; les montants restent des Decimal.
"""Synchronisation incrémentale des factures vers un logiciel comptable (journal des ventes CSV).
Incremental invoice sync to accounting software (sales journal as CSV).
Principe : on relit les factures modifiées depuis le dernier passage (filtre
``updated_after``), avec un recouvrement de 5 minutes, et on dédoublonne par
identifiant de facture : une facture déjà écrite n'est pas réécrite. La table de
correspondance des comptes est celle de VOTRE plan comptable : Tauraco ne reclasse
aucun compte. Les montants restent des chaînes décimales (``Decimal``), jamais des
flottants.
Usage : TAURACO_CLIENT_ID=… TAURACO_CLIENT_SECRET=… python3 synchro_comptable.py
"""
from __future__ import annotations
import csv
import json
import os
from datetime import datetime, timedelta, timezone
from decimal import Decimal
from pathlib import Path
from typing import Iterable, Iterator, List
from client_credentials import get_token, iterate
STATE_FILE = Path(os.environ.get("SYNC_STATE", "etat_synchro.json"))
OUTPUT_FILE = Path(os.environ.get("SYNC_OUTPUT", "journal_ventes.csv"))
OVERLAP = timedelta(minutes=5)
#: Correspondance déclarée par l'entreprise (exemple SYSCOHADA : 411 clients, 701 ventes).
ACCOUNT_CUSTOMERS = os.environ.get("ACCOUNT_CUSTOMERS", "411100")
ACCOUNT_SALES = os.environ.get("ACCOUNT_SALES", "701100")
ACCOUNT_VAT = os.environ.get("ACCOUNT_VAT", "443100")
SYNCED_STATUSES = {"SENT", "PARTIALLY_PAID", "PAID", "OVERDUE"}
def journal_lines(invoice: dict) -> List[List[str]]:
"""Trois lignes équilibrées d'une facture : débit client, crédit ventes HT, crédit TVA."""
total = Decimal(invoice["total"])
subtotal = Decimal(invoice.get("subtotal") or invoice["total"])
tax = Decimal(invoice.get("tax_total") or "0")
if subtotal + tax != total:
raise ValueError(f"facture {invoice['number']} : HT + taxes ≠ TTC, à vérifier")
label = f"{invoice['number']} {invoice.get('customer_name') or ''}".strip()
rows = [[invoice["issue_date"], "VT", invoice["number"], ACCOUNT_CUSTOMERS, label, str(total), "0"],
[invoice["issue_date"], "VT", invoice["number"], ACCOUNT_SALES, label, "0", str(subtotal)]]
if tax:
rows.append([invoice["issue_date"], "VT", invoice["number"], ACCOUNT_VAT, label, "0", str(tax)])
return rows
def new_invoices(invoices: Iterable[dict], already_synced: set) -> Iterator[dict]:
for invoice in invoices:
if invoice["status"] in SYNCED_STATUSES and invoice["id"] not in already_synced:
already_synced.add(invoice["id"])
yield invoice
def main() -> None:
state = json.loads(STATE_FILE.read_text()) if STATE_FILE.exists() else {"since": None, "synced": []}
started = datetime.now(timezone.utc)
params = {"limit": "200"}
if state["since"]:
since = datetime.fromisoformat(state["since"].replace("Z", "+00:00")) - OVERLAP
params["updated_after"] = since.isoformat().replace("+00:00", "Z")
token = get_token(os.environ["TAURACO_CLIENT_ID"], os.environ["TAURACO_CLIENT_SECRET"],
"invoices:read", tenant=os.environ.get("TAURACO_TENANT"))["access_token"]
synced = set(state["synced"])
with OUTPUT_FILE.open("a", newline="", encoding="utf-8") as handle:
writer = csv.writer(handle, delimiter=";")
for invoice in new_invoices(iterate("/invoices", token, params), synced):
writer.writerows(journal_lines(invoice))
state = {"since": started.isoformat().replace("+00:00", "Z"), "synced": sorted(synced)}
STATE_FILE.write_text(json.dumps(state))
if __name__ == "__main__":
main()
Il s'appuie sur le client de l'exemple python/client_credentials.py (même répertoire).
Formats métier
- FEC : disponible pour une entreprise au régime comptable français (
fec), refusé ailleurs. - Factur-X (CII EN 16931) : développé, livré « bêta, non validé par Schematron ». UBL différé.
- ISO 20022 :
camt.053(relevés) développé, « expérimental, non validé » ;pain.001(virements) non pris en charge en v1, aucune coordonnée bancaire de bénéficiaire n'étant servie ; la validation XSD ne s'active que si les schémas officiels sont déposés par l'opérateur.
Aucune conformité n'est déclarée pour ces formats.
Sécurité des connexions sortantes
Les adresses de vos serveurs (REST, Odoo, SFTP) sont contrôlées à la création et à chaque exécution : résolution DNS complète, adresses publiques seulement, ports autorisés (443/8443 en HTTPS, 22/2222 en SFTP), aucune redirection. Les secrets de connexion sont chiffrés et jamais réaffichés. Un test de connexion rend un message générique (unreachable, host_key_mismatch, auth_failed, timeout), jamais la bannière du serveur distant.