01 Modèle & périmètre
Deux niveaux de données coexistent : le fournisseur, propre à une copropriété, et le super-fournisseur, propre au portefeuille (le « domaine ») qui regroupe plusieurs copropriétés. La consolidation ne fait que poser un pont entre les deux.
/v2/domaines/{id}/fournisseurssuper vers un super-fournisseur — c'est l'unique lien entre les deux niveaux./v2/domaines/{id}/super-fournisseurslibelle, referenceUnique — SIRET ou BCE en Belgique, iban, bic, modePaiement, delaiReglement) et la liste fournisseurs des fiches qu'il regroupe. Rattaché à un seul domaine.L'unicité de referenceUnique (SIRET/BCE) au sein d'un même domaine est vérifiée par l'API à la validation de chaque écriture (§6), mais ce n'est pas une garantie transactionnelle absolue : deux créations strictement simultanées avec la même référence ne sont pas nécessairement bloquées l'une par l'autre. À traiter comme une validation de bon usage, pas comme un verrou.
1.1 Une précision de vocabulaire
L'API expose aussi, sur ce même objet, des endpoints POST/PUT/DELETE .../super-fournisseurs/{id}/contacts qui consolident les interlocuteurs (personnes) rattachés à un super-fournisseur — un mécanisme distinct, non couvert par ce guide, qui consolide les fiches fournisseur elles-mêmes (numéro de compte, IBAN, référence unique) et non leurs contacts humains. Les deux partagent le même principe (une entité « super » au niveau domaine, alimentée depuis des fiches locales à une copropriété) mais ne portent pas sur le même objet — à ne pas confondre si vous cherchez à consolider des personnes plutôt que des fiches fournisseur.
02 Contrôle d'accès
Chaque appel documenté ici est validé à deux niveaux avant toute lecture ou écriture :
- le client OAuth doit être explicitement autorisé sur le domaine
{idDomaine}de l'URL appelée ; - pour toute opération portant sur un super-fournisseur précis, ce super-fournisseur doit réellement appartenir à ce même domaine.
Si l'une des deux conditions échoue : 403. Un client OAuth n'a donc jamais accès à un super-fournisseur en dehors des domaines pour lesquels il est explicitement habilité, et ne peut pas « deviner » un identifiant d'un autre portefeuille pour y accéder via son propre domaine — la seconde vérification l'empêche même si la première passait.
Toutes les routes de ce guide sont préfixées /v2 et servies depuis https://syndic.datacopro.org en production, ou https://preprod-syndic.datacopro.org sur la plateforme de test.
03 Parcours fonctionnels
Chaque parcours correspond à un cas d'usage réel, dans l'ordre où les appels sont réellement enchaînés, avec le paramètre qui compte et sa raison d'être.
3.1 Repérer les fournisseurs à consolider
Cas d'usage : lister les fournisseurs qui n'appartiennent encore à aucun super-fournisseur — le point de départ naturel avant de lancer une consolidation. Illustration : c'est ainsi qu'un logiciel existant (Copromatic) alimente son écran « fournisseurs à consolider ».
'' → ne retourne que les fournisseurs dont la relation super est nulle (voir §5 pour la syntaxe des filtres sur une relation absente).[id, libelle, libelleComplement, tags, modePaiement, note, dateCreation, createurContexte.*, copro.id, copro.libelle] — liste fermée et explicite.displays précis
Sans displays, ou avec seulement des exclusions (!champ), l'API recalcule pour chaque fournisseur son solde comptable en détail — coûteux sur un portefeuille de plusieurs centaines de fournisseurs, et inutile ici puisque ce cas d'usage n'a pas besoin du solde. Passer une liste d'inclusions explicite sans solde dedans désactive ce calcul.
3.2 Créer un super-fournisseur
Deux origines possibles : à partir d'un fournisseur déjà repéré comme non consolidé (pré-remplissage), ou de zéro. Dans les deux cas, la création se fait en un seul appel — l'éventuel rattachement initial fait partie du même payload.
libelle, libelleComplement, referenceUnique, iban, bic, modePaiement, delaiReglement, tags, langue depuis le fournisseur d'origine — simple confort de saisie côté client, sans appel API dédié au-delà du GET du fournisseur déjà fait pour l'afficher.{
"superfournisseur": {
"libelle": "Ascenseurs Dupont",
"libelleComplement": "",
"referenceUnique": "812345678", // SIRET (ou BCE si domaine belge)
"referenceInterne": "",
"tags": ["Ascenseur"],
"bic": "BNPAFRPP",
"iban": "FR7630004...",
"delaiReglement": 30,
"modePaiement": "Virement",
"note": "",
"fournisseurs": [4821] // ids de fournisseurs à rattacher dès la création
},
"fournisseurs": { "libelle": 1, "referenceUnique": 1 }
}
1 est recopié depuis le super-fournisseur vers chaque fournisseur rattaché, immédiatement après la création (voir encadré ci-dessous).Consolider un fournisseur, c'est vouloir qu'il porte visiblement le libellé et la référence « officiels » du super-fournisseur dans sa copropriété d'origine (états financiers, export comptable) plutôt que son ancien libellé local. L'API le fait pour vous si vous le demandez explicitement, champ par champ, via ce tableau racine — elle ne le fait jamais implicitement.
Champs éligibles à cette recopie : libelle, referenceUnique, referenceInterne, tags, iban, bic, modePaiement, delaiReglement. libelleComplement n'en fait pas partie côté API — à recopier vous-même si vous en avez besoin, comme le fait l'étape 3 du parcours de détachement (§3.7) dans l'autre sens.
Si la recopie change le libelle d'un fournisseur, le libellé de son compte de plan comptable (poste 4010x) est régénéré automatiquement pour rester cohérent avec le nouveau nom — un changement visible dans les exports comptables de la copropriété concernée, pas seulement dans la fiche fournisseur.
3.3 Rattacher des fournisseurs supplémentaires (consolider)
Cas d'usage : à partir d'une sélection de fournisseurs non consolidés (§3.1) ou d'une recherche libre, les rattacher à un super-fournisseur déjà existant.
[id, libelle, libelleComplement, referenceUnique] — le strict nécessaire pour construire un libellé de menu déroulant.[id, libelle, libelleComplement, super.id, copro.libelle] — super.id sert uniquement à repérer un fournisseur déjà consolidé ailleurs, pour prévenir avant un rattachement croisé. Étape inutile si la sélection de fournisseurs à rattacher est déjà connue (par exemple issue directement du parcours §3.1).[id, libelle, fournisseurs.id]{
"superfournisseur": {
"fournisseurs": [4821, 5390, 6104] // anciens + nouveaux, fusionnés côté client
},
"fournisseurs": { "libelle": 1, "referenceUnique": 1 }
}
superfournisseur.fournisseurs définit l'ensemble exact des fournisseurs rattachés après l'appel — ce n'est pas une liste à ajouter. Envoyer uniquement les 2 nouveaux ids détacherait silencieusement tous les fournisseurs déjà rattachés. C'est pour ça que l'étape 3 est indispensable : l'appelant doit toujours reconstruire la liste complète (anciens + nouveaux) côté client avant d'appeler ce PUT.
Risque de perte de mise à jour concurrente : le cycle lire la liste actuelle → fusionner → réécrire l'ensemble complet n'est protégé par aucun verrou ni jeton de version — le même principe que la course décrite en §3.8 pour la suppression, mais plus dommageable ici puisqu'il peut effacer un rattachement fait entre-temps par quelqu'un d'autre plutôt que de simplement échouer. Si deux appels concurrents consolident vers le même super-fournisseur, le second PUT écrase la liste du premier sans avertissement. Relire l'état juste avant d'écrire réduit la fenêtre de risque, mais ne l'élimine pas.
3.4 Modifier un super-fournisseur existant
Cas d'usage : changer le libellé, l'IBAN, le mode de paiement, etc. d'un super-fournisseur déjà créé.
superfournisseur.fournisseurs laisse la liste de rattachement inchangée ; la renvoyer reconstituée à l'identique produit le même résultat, à condition de repartir de la liste lue à l'étape 1. Le tableau racine fournisseurs: {libelle:1, referenceUnique:1} reste utile même sans toucher au rattachement : c'est ce qui répercute un changement de libellé ou de référence sur toutes les fiches fournisseur déjà consolidées.3.5 Consulter la fiche consolidée
Cas d'usage : afficher la fiche consolidée — les fiches fournisseur rattachées (une par copropriété) et les contrats associés.
displays ici (contrairement à §3.1/3.3) : ce cas d'usage a justement besoin du solde par fournisseur et du solde global, donc le calcul de solde n'a aucune raison d'être désactivé.contrat.fournisseur.super = {id} côté serveur — il ramène en un appel les contrats de toutes les copropriétés où ce super-fournisseur a une fiche, sans que le client ait à boucler copro par copro.3.6 Comptabilité consolidée (soldes & factures)
Cas d'usage : obtenir le total facturé et le solde comptable cumulés du super-fournisseur, avec un filtre de date de fin et un filtre « toutes les copropriétés sauf... ».
YYYY-MM-DD 23:59:59) — le solde est recalculé à cette date, pas seulement à aujourd'hui.[montant, date, exercice.copro.id] — le strict nécessaire pour sommer un montant et filtrer par date/copropriété côté client.Il est possible d'obtenir directement le même résultat filtré en passant un filters serveur (§5, ex. exercice.copro.id__eq, ou un filtre de date sur date) plutôt que de récupérer tout l'historique du super-fournisseur pour le filtrer après coup — nettement plus léger sur un super-fournisseur qui consolide beaucoup de copropriétés et d'exercices.
Pour n'afficher qu'un total agrégé, les deux appels ci-dessus suffisent. Pour construire un tableau détaillé ligne à ligne, paginé, avec un chargement progressif sur un grand portefeuille, il est possible de ne pas passer par l'endpoint consolidé et d'interroger plutôt les factures/règlements copropriété par copropriété, avec pagination côté serveur (§5) — un choix d'implémentation à faire selon le volume attendu, l'endpoint consolidé restant la voie la plus directe pour un simple total.
3.7 Détacher un fournisseur
Cas d'usage : retirer un fournisseur d'un super-fournisseur, en lui laissant des valeurs propres cohérentes plutôt qu'un vide.
superfournisseur.fournisseurs = liste actuelle moins l'id détaché. Pas de fournisseurs racine ici : on ne veut plus rien recopier vers un fournisseur qu'on retire.libelleComplement, referenceInterne, tags, bic, iban, delaiReglement, modePaiement) sur le fournisseur qui vient d'être détaché.L'API ne fait aucune recopie inverse au détachement : elle se contente de vider la relation super du fournisseur. Un fournisseur détaché garde donc telles quelles les valeurs qui lui avaient été poussées lors de son rattachement (§3.2/3.3) — sauf action explicite du client. C'est cette étape 3, entièrement à la charge de l'appelant, qui « fige » certaines de ces valeurs sur le fournisseur avant la coupure du lien, pour qu'il conserve une fiche cohérente plutôt qu'un IBAN orphelin du super-fournisseur.
Deux champs restent volontairement hors de cette liste, dans les deux sens : l'étape 3 fige libelleComplement, referenceInterne, tags, bic, iban, delaiReglement, modePaiement — mais pas libelle ni referenceUnique, alors que ce sont justement les deux champs que la recopie d'attachement (§3.2/3.4) pousse par défaut. Ni l'API, ni cette implémentation de référence ne remettent ces deux champs à jour au détachement : un fournisseur détaché continue donc, indéfiniment, de porter le libelle et la referenceUnique « officiels » du super-fournisseur dont il vient pourtant de se dissocier, jusqu'à ce qu'un appel PUT explicite sur sa propre fiche les change.
Constaté sur la version actuellement en service de l'API : si le super-fournisseur ne contient plus qu'un seul fournisseur et qu'on le détache (donc superfournisseur.fournisseurs envoyé vide), le détachement peut n'avoir silencieusement aucun effet en base — alors que l'appel PUT répond avec succès. Ce comportement peut être corrigé à l'avenir sans préavis particulier. En attendant, un client qui automatise le détachement devrait systématiquement vérifier après coup, par une relecture (GET), que le fournisseur n'apparaît plus dans la liste fournisseurs du super-fournisseur, plutôt que de faire confiance au seul code HTTP de succès.
3.8 Supprimer un super-fournisseur
Nettoyage d'un super-fournisseur devenu vide (tous ses fournisseurs détachés).
fournisseurs est bien vide, pour afficher un message clair avant même de tenter la suppression.400 avec le message « Des fournisseurs sont associés a ce super-fournisseur. » si au moins un fournisseur est encore rattaché. La vérification côté client (étape 1) n'est qu'un confort d'affichage, jamais une garantie : entre les deux appels, un autre utilisateur peut rattacher un fournisseur.04 Référence des endpoints
Tous préfixés /v2/domaines/{idDomaine}. Format de sortie .json (aussi disponible en .xml via {_format}).
| Méthode | Route | Rôle |
|---|---|---|
| GET | /fournisseurs.json | Lister les fournisseurs du domaine (tous, ou filtrés par super, copro, etc.) |
| GET | /fournisseurs/{id}.json | Détail d'un fournisseur, vu depuis le domaine |
| GET | /super-fournisseurs.json | Lister les super-fournisseurs du domaine |
| POST | /super-fournisseurs.json | Créer un super-fournisseur (avec rattachement initial optionnel) |
| GET | /super-fournisseurs/{id}.json | Détail : champs de référence + fournisseurs rattachés + solde |
| PUT | /super-fournisseurs/{id}.json | Modifier les champs et/ou remplacer la liste de rattachement |
| DELETE | /super-fournisseurs/{id}.json | Supprimer (uniquement si plus aucun fournisseur rattaché) |
| GET | /super-fournisseurs/{id}/factures.json | Factures agrégées de tous les fournisseurs rattachés |
| GET | /super-fournisseurs/{id}/reglements.json | Opérations bancaires agrégées |
| GET | /super-fournisseurs/{id}/contrats.json | Contrats agrégés |
| POST | /super-fournisseurs/{id}/contacts.json | Rattacher un super-contact (hors périmètre de ce guide, §1.1) |
| PUT | /super-fournisseurs/{id}/contacts/{idContact}.json | Modifier un rattachement de super-contact |
| DELETE | /super-fournisseurs/{id}/contacts/{idContact}.json | Détacher un super-contact |
05 displays & filters
Deux query params génériques à tous les endpoints de liste et de détail de l'API v2, utilisés massivement dans les parcours ci-dessus.
5.1 displays
Liste de noms de champs (notation pointée pour une relation, ex. fournisseurs.id). Un préfixe ! exclut au lieu d'inclure. Règle de bascule : s'il n'y a que des exclusions, tout le reste reste inclus par défaut ; dès qu'une seule inclusion explicite apparaît, tout ce qui n'est pas listé est exclu.
Pour super-fournisseurs.json, super-fournisseurs/{id}.json et fournisseurs.json, l'API tient compte du contenu de displays avant de construire la réponse pour décider si elle doit calculer le solde/solde4010 de chaque entité (un calcul comptable détaillé, un par fournisseur). Omettre displays « parce qu'on veut tout » déclenche donc ce calcul même si le solde n'est pas utilisé — toujours passer une liste d'inclusions explicite quand le solde n'est pas nécessaire.
5.2 filters
Tableau associatif, clé au format champ ou champ__operateur. Plusieurs clés au même niveau = ET ; un tableau indexé numériquement de groupes = OU de ces groupes.
| Opérateur | Alias | Types compatibles |
|---|---|---|
eq | = == equals equal | string, number, date, boolean |
diff | != ! <> neq | string, number, date, boolean |
gt / gte | > / >= => | number, date |
lt / lte | < / <= =< | number, date |
contains / ncontains | — | string |
empty / nempty | — | string, number, date, boolean, object (relation vide/non vide) |
Sans opérateur, le filtre reste utilisable mais s'applique seulement après que l'ensemble des résultats a déjà été constitué côté serveur — c'est le cas de filters[super]='' utilisé en §3.1, qui se résout en pratique comme un empty sur la relation.
Un filtre sans opérateur ne réduit pas le travail déjà fait pour constituer la réponse complète avant de la filtrer. Avec l'opérateur explicite (__eq, __gte…), le filtre peut être appliqué directement lors de la construction de la réponse, pour les champs qui s'y prêtent (pas systématiquement le cas des relations profondément imbriquées ni des valeurs calculées comme un solde) — nettement plus léger sur un domaine avec un grand nombre de fournisseurs ou de factures.
06 Limites & pièges connus
Résumé transverse des contraintes rencontrées dans les parcours ci-dessus — à garder sous la main pour toute implémentation de ce contrat d'API.
- Le
PUTremplace, il n'ajoute pas.superfournisseur.fournisseursdoit toujours porter la liste complète désirée après l'appel ; oublier un id déjà rattaché le détache. Ce cycle lire-fusionner-réécrire n'est protégé par aucun verrou de concurrence : deux écritures concurrentes sur le même super-fournisseur peuvent silencieusement s'écraser l'une l'autre. libelleetreferenceUniquene sont jamais restaurés au détachement, ni par l'API ni par la recopie manuelle de référence (§3.7) — contrairement àlibelleComplement,tags,iban,bic,modePaiement,delaiReglement,referenceInterne. Un fournisseur détaché continue donc d'afficher le libellé et la référence du super-fournisseur, sauf correction explicite.referenceUniqueobligatoire et unique, mais seulement par domaine. Contrôle effectué par l'API à la validation de chaque écriture, pas garanti de façon transactionnelle — deux créations strictement concurrentes avec la même référence ne sont pas nécessairement bloquées l'une par l'autre.- La suppression est bloquée (HTTP 400) tant qu'au moins un fournisseur reste rattaché ; il faut détacher un à un avant de supprimer.
- La recopie de champs (tableau racine
fournisseurs) n'est jamais implicite et ne s'applique qu'à l'attachement/la modification, jamais au détachement — toute logique de « figer les valeurs avant de couper le lien » est à la charge du client (§3.7). - Bug actif : détacher le dernier fournisseur d'un super-fournisseur ne modifie rien en base malgré une réponse HTTP de succès (voir §3.7) — vérifier le résultat par une relecture, pas par le seul code retour.
- Aucune limite de volumétrie n'est imposée sur le nombre de fournisseurs rattachables à un super-fournisseur, ni sur le nombre de copropriétés distinctes concernées, dans les limites du domaine.
- Le calcul de solde est le poste de coût dominant des endpoints de cette famille : toujours passer un
displaysen liste d'inclusions explicite quand le solde n'est pas affiché.
07 Questions de validation
Trois questions opérationnelles qu'un développeur tiers se poserait concrètement en implémentant ce flux, vérifiées par relecture adversariale (doubt-driven-development) du contenu ci-dessus.
Si je veux rattacher un nouveau fournisseur à un super-fournisseur qui en a déjà dix, dois-je renvoyer les dix existants dans mon appel, ou juste le nouveau ?
Les dix existants et le nouveau. superfournisseur.fournisseurs est un remplacement complet de l'ensemble rattaché, jamais un ajout — documenté explicitement en §3.3 (encadré « Remplacement complet ») et rappelé en §6. C'est pourquoi le parcours impose un GET préalable pour connaître la liste actuelle avant toute écriture — en gardant à l'esprit que ce cycle lire-fusionner-réécrire n'est pas protégé contre un autre appel concurrent sur le même super-fournisseur (voir l'avertissement dans le même encadré §3.3).
Après avoir détaché un fournisseur, est-ce que ses champs (IBAN, référence, libellé) reviennent automatiquement à ce qu'ils étaient avant sa consolidation ?
Non — avec deux nuances à ne pas manquer. D'abord, l'API elle-même n'y touche pas au détachement : elle vide seulement la relation super (§3.7), et ne restaure jamais rien. Ensuite, même l'étape de recopie manuelle décrite en §3.7 ne couvre pas libelle ni referenceUnique : ces deux champs précis restent donc durablement ceux du super-fournisseur, sans qu'aucune étape documentée ici ne les corrige (détail dans l'encadré « Deux champs restent volontairement hors de cette liste », §3.7). Et sur un super-fournisseur qui ne compte plus qu'un seul fournisseur, le comportement décrit juste en dessous (§3.7) va plus loin : même la relation super peut ne pas revenir à vide, malgré une réponse HTTP de succès — ne jamais déduire l'effet réel du seul code retour, toujours relire l'état après un détachement sensible.
Peut-on consolider deux fournisseurs qui appartiennent à deux domaines (portefeuilles) différents ?
Non. Un super-fournisseur appartient à un seul domaine (§1), et le contrôle d'accès (§2) vérifie sur chaque appel que le super-fournisseur ciblé appartient bien au domaine de la requête. Un id de fournisseur d'un autre domaine, même envoyé explicitement dans superfournisseur.fournisseurs, n'est simplement pas résolu par l'API : la portée « un domaine » est un plancher, pas une simple convention d'interface. À noter : dans ce cas, l'appel PUT ne renvoie pas une erreur par id — il répond simplement un succès dans lequel l'id étranger au domaine est absent de la liste résultante, un peu comme le comportement décrit en §3.7 peut masquer un non-effet derrière un code HTTP de succès. Toujours relire la liste fournisseurs retournée plutôt que supposer qu'elle contient tout ce qui a été envoyé.