🍫 Cacaomatic API du pseudo-ERP · TP R5.MTD.14

Sujet du TP R5.MTD.14 · Interopérabilité

Instructions à destination des étudiants. Le détail des appels est dans la documentation.

Télécharger la version hors ligne (sujet + documentation) À enregistrer avant la séance : elle sert de plan de secours si la connexion est coupée.

Contexte

Cacaomatic est une entreprise fictive qui fabrique du chocolat. Deux systèmes doivent travailler ensemble sans se connaître :

  • la boutique en ligne, qui reçoit les commandes des clients, dans son format (sku, cartons ou pièces, dates jj/mm/aaaa) ;
  • l'ERP de l'usine, qui gère commandes, stocks, rĂ©servations et fabrication, dans son format (codes article, unitĂ©s, dates aaaa-mm-jj).

Votre équipe construit, dans n8n, les workflows qui font dialoguer les deux : lire les commandes de la boutique, les traduire, les passer à l'ERP, puis renvoyer le résultat à la boutique. Les deux systèmes sont des API (https://cacaomatic.alt4b.fr/api).

Chaque équipe (A à H) dispose de sa clé d'API et de ses propres données : vous ne pouvez pas perturber les autres équipes.

1. Se connecter Ă  n8n

  • Adresse : https://n8n-tp.alt4b.fr
  • Identifiant : l'adresse e-mail reçue par invitation (ou communiquĂ©e par l'enseignant) ; mot de passe : celui dĂ©fini Ă  l'activation du compte.
  • Chaque groupe a son propre compte : vous ne voyez que vos workflows et vos credentials.

2. Créer le credential de l'API Cacaomatic (une seule fois)

  1. Vérifiez d'abord que l'API répond : /api/ping (sans clé).
  2. Menu de gauche → Credentials → Create credential → cherchez Header Auth.
  3. Name : Authorization ; Value : Bearer <votre clé d'équipe> (avec l'espace après Bearer).
  4. Save, puis nommez-le par exemple « Cacaomatic équipe X ».
  5. Dans un nœud HTTP Request : Authentication « Generic Credential Type » → Header Auth → ce credential. Adresse de base : https://cacaomatic.alt4b.fr/api.

Le mĂŞme credential sert pour la boutique et pour l'ERP.

3. Premier workflow : lire la boutique et un stock

  1. Workflows → Create workflow, nommez-le « Test équipe X ».
  2. Add first step → Trigger manually.
  3. Cliquez sur le + → HTTP Request : méthode GET, URL https://cacaomatic.alt4b.fr/api/boutique/commandes, credential de l'étape 2. Execute step : vous voyez les commandes de la boutique.
  4. Ajoutez un second HTTP Request : GET https://cacaomatic.alt4b.fr/api/stocks/TAB-001. Vous obtenez stock_unites, reserve et disponible.
  5. Un code article = un type de produit (TAB tablette, BAR barre, BTE boîte, PRA praliné, SAC sachet, POU poudre, PAT pâte à tartiner, CAL calendrier) et un numéro. La liste complète est dans l'onglet Stocks.
  6. Vérifiez vos appels dans l'onglet Journal de votre espace équipe, et les commandes dans l'onglet Boutique.

4. Le format de la boutique

La boutique publie ses commandes au fil du temps : relancez la lecture régulièrement (a_venir dit combien restent à venir). Une commande ressemble à :

{
  "id": 1,
  "id_commande_web": "WEB-2026-0001",
  "passee_le": "24/10/2026 09:12",
  "client": {"nom": "Épicerie Fictive du Port", "email": "achats@epicerie-port.example"},
  "livraison_souhaitee": "15/12/2026",
  "articles": [
    {"sku": "CHOC-LAIT-100", "designation": "Tablette Lait Onctueux", "quantite": 2, "conditionnement": "carton"},
    {"sku": "BARRE-CEREALES-40", "designation": "Barre Croquante aux Céréales", "quantite": 10, "conditionnement": "piece"}
  ],
  "statut": "nouvelle"
}

Pour la passer Ă  l'ERP, il faut traduire : le sku en code_article (table ci-dessous), piece en unite, la date en aaaa-mm-jj, id_commande_web en reference_client, client.nom en client.

Table de correspondance sku → code article

sku (boutique)Désignationcode_article (ERP)
CHOC-NOIR-70-100Tablette Noir Intense 70 %TAB-001
CHOC-LAIT-100Tablette Lait OnctueuxTAB-002
CHOC-BLANC-100Tablette Blanc VanilleTAB-003
CHOC-NOISETTE-100Tablette Lait Noisettes EntièresTAB-004
BARRE-CARAMEL-45Barre Caramel FondantBAR-001
BARRE-CEREALES-40Barre Croquante aux CéréalesBAR-002
COFFRET-TRUFFES-250Coffret de Truffes AssortiesBTE-001
COFFRET-PRALINES-500Grand Coffret de PralinésPRA-001
PEPITES-NOIR-200Pépites de Chocolat NoirSAC-001
CACAO-POUDRE-250Cacao en PoudrePOU-001
PATE-TARTINER-350Pâte à Tartiner Chocolat-NoisettePAT-001
ROCHERS-PRALINES-1KGRochers Pralinés (vrac 1 kg)PRA-002
CALENDRIER-AVENTCalendrier de l'AventCAL-001
OEUFS-FOURRES-200Oeufs Fourrés AssortisSAC-002
COUVERTURE-NOIR-1KG-PROChocolat de Couverture Noir 1 kg (pro)SAC-003
Même table en JSON (à coller dans un nœud « Code » ou « Edit Fields »)
{
    "CHOC-NOIR-70-100": "TAB-001",
    "CHOC-LAIT-100": "TAB-002",
    "CHOC-BLANC-100": "TAB-003",
    "CHOC-NOISETTE-100": "TAB-004",
    "BARRE-CARAMEL-45": "BAR-001",
    "BARRE-CEREALES-40": "BAR-002",
    "COFFRET-TRUFFES-250": "BTE-001",
    "COFFRET-PRALINES-500": "PRA-001",
    "PEPITES-NOIR-200": "SAC-001",
    "CACAO-POUDRE-250": "POU-001",
    "PATE-TARTINER-350": "PAT-001",
    "ROCHERS-PRALINES-1KG": "PRA-002",
    "CALENDRIER-AVENT": "CAL-001",
    "OEUFS-FOURRES-200": "SAC-002",
    "COUVERTURE-NOIR-1KG-PRO": "SAC-003"
}

Un sku absent de cette table est une anomalie Ă  signaler, pas Ă  deviner.

Phase 0 · Concevoir avant de construire

Avant d'ouvrir n8n, on raisonne sur le papier (ou dans un document) : quels appels, dans quel ordre, avec quelles données, et que faire quand ça se passe mal ? Cette phase ne demande ni n8n ni l'API : elle se fait sans connexion avec ce sujet et la documentation. Elle sert aussi de plan de secours : si les outils sont indisponibles pendant la séance, c'est le TP du jour, et ce que vous concevez ici se réalise ensuite dans n8n.

Les données de départ

Extrait du catalogue de l'ERP (stock initial et unités par carton). Chaque équipe démarre avec les mêmes stocks.

code_articleLibelléStock initial (unités)Unités par carton
TAB-001Tablette chocolat noir 70 % 100 g40020
TAB-002Tablette chocolat au lait 100 g50020
TAB-003Tablette chocolat blanc 100 g18020
TAB-004Tablette chocolat noisettes 100 g6020
BAR-001Barre chocolatée caramel 45 g30036
BAR-002Barre croquante céréales 40 g72036
BTE-001Boîte de truffes assorties 250 g9612
PRA-001Boîte de pralinés 500 g488
SAC-001Sachet de pépites de chocolat noir 200 g24024
POU-001Cacao en poudre 250 g15012
PAT-001Pâte à tartiner chocolat-noisette 350 g1206
PRA-002Rochers pralinés en vrac 1 kg3510
CAL-001Calendrier de l'Avent chocolaté06
SAC-002Oeufs fourrés assortis 200 g9015
SAC-003Chocolat de couverture noir 1 kg (pro)2510

Les 8 commandes que la boutique publie, dans l'ordre d'arrivée (à traiter dans cet ordre, les stocks se consomment au fur et à mesure) :

CommandeClientLivraisonArticles (sku × quantité)
WEB-2026-0001Épicerie Fictive du Port15/12/2026 CHOC-LAIT-100 × 2 cartons
BARRE-CEREALES-40 × 10 pièces
WEB-2026-0002Biscuiterie Imaginaire20/12/2026 CHOC-NOIR-70-100 Ă— 5 cartons
WEB-2026-0003Café du Marché10/12/2026 BARRE-CARAMEL-45 × 300 pièces
WEB-2026-0004Boutique Les Délices Fictifs18/12/2026 CHOC-NOISETTE-100 × 61 pièces
WEB-2026-0005HĂ´tel Fictif des Alpes22/12/2026 CALENDRIER-AVENT Ă— 6 cartons
CHOC-BLANC-100 Ă— 1 carton
WEB-2026-0006Épicerie Fictive du Port19/12/2026 SAISON-NOEL-2025 × 1 pièce
WEB-2026-0002Biscuiterie Imaginaire20/12/2026 CHOC-NOIR-70-100 Ă— 5 cartons
WEB-2026-0008Traiteur Fictif Chocolat Pro23/12/2026 COFFRET-PRALINES-500 Ă— 4 cartons
CACAO-POUDRE-250 Ă— 2 cartons
PEPITES-NOIR-200 × 30 pièces
Les mĂŞmes commandes en JSON (format de la boutique)
[
    {
        "id_commande_web": "WEB-2026-0001",
        "client": {
            "nom": "Épicerie Fictive du Port",
            "email": "achats@epicerie-port.example"
        },
        "livraison_souhaitee": "15/12/2026",
        "articles": [
            {
                "sku": "CHOC-LAIT-100",
                "quantite": 2,
                "conditionnement": "carton"
            },
            {
                "sku": "BARRE-CEREALES-40",
                "quantite": 10,
                "conditionnement": "piece"
            }
        ]
    },
    {
        "id_commande_web": "WEB-2026-0002",
        "client": {
            "nom": "Biscuiterie Imaginaire",
            "email": "commandes@biscuiterie.example"
        },
        "livraison_souhaitee": "20/12/2026",
        "articles": [
            {
                "sku": "CHOC-NOIR-70-100",
                "quantite": 5,
                "conditionnement": "carton"
            }
        ]
    },
    {
        "id_commande_web": "WEB-2026-0003",
        "client": {
            "nom": "Café du Marché",
            "email": "cafe@marche.example"
        },
        "livraison_souhaitee": "10/12/2026",
        "articles": [
            {
                "sku": "BARRE-CARAMEL-45",
                "quantite": 300,
                "conditionnement": "piece"
            }
        ]
    },
    {
        "id_commande_web": "WEB-2026-0004",
        "client": {
            "nom": "Boutique Les Délices Fictifs",
            "email": "contact@delices.example"
        },
        "livraison_souhaitee": "18/12/2026",
        "articles": [
            {
                "sku": "CHOC-NOISETTE-100",
                "quantite": 61,
                "conditionnement": "piece"
            }
        ]
    },
    {
        "id_commande_web": "WEB-2026-0005",
        "client": {
            "nom": "HĂ´tel Fictif des Alpes",
            "email": "direction@hotel-alpes.example"
        },
        "livraison_souhaitee": "22/12/2026",
        "articles": [
            {
                "sku": "CALENDRIER-AVENT",
                "quantite": 6,
                "conditionnement": "carton"
            },
            {
                "sku": "CHOC-BLANC-100",
                "quantite": 1,
                "conditionnement": "carton"
            }
        ]
    },
    {
        "id_commande_web": "WEB-2026-0006",
        "client": {
            "nom": "Épicerie Fictive du Port",
            "email": "achats@epicerie-port.example"
        },
        "livraison_souhaitee": "19/12/2026",
        "articles": [
            {
                "sku": "SAISON-NOEL-2025",
                "quantite": 1,
                "conditionnement": "piece"
            }
        ]
    },
    {
        "id_commande_web": "WEB-2026-0002",
        "client": {
            "nom": "Biscuiterie Imaginaire",
            "email": "commandes@biscuiterie.example"
        },
        "livraison_souhaitee": "20/12/2026",
        "articles": [
            {
                "sku": "CHOC-NOIR-70-100",
                "quantite": 5,
                "conditionnement": "carton"
            }
        ]
    },
    {
        "id_commande_web": "WEB-2026-0008",
        "client": {
            "nom": "Traiteur Fictif Chocolat Pro",
            "email": "cuisine@traiteur.example"
        },
        "livraison_souhaitee": "23/12/2026",
        "articles": [
            {
                "sku": "COFFRET-PRALINES-500",
                "quantite": 4,
                "conditionnement": "carton"
            },
            {
                "sku": "CACAO-POUDRE-250",
                "quantite": 2,
                "conditionnement": "carton"
            },
            {
                "sku": "PEPITES-NOIR-200",
                "quantite": 30,
                "conditionnement": "piece"
            }
        ]
    }
]

Exemples de réponses de l'API

Réponses réelles, à connaître pour concevoir les tests de votre workflow. Toutes les erreurs ont le même format {"erreur", "message", "details"}.

201 · POST /api/commandes (cartons déjà convertis en unités par l'ERP)
{"id": 21, "reference_client": "WEB-2026-0002", "client": "Biscuiterie Imaginaire",
 "date_livraison_souhaitee": "2026-12-20", "statut": "recue",
 "lignes": [{"code_article": "TAB-001", "quantite": 5, "unite": "carton", "quantite_unites": 100}]}
201 · POST /api/reservations
{"id": 13, "reference_commande": "WEB-2026-0002", "lignes": [{"code_article": "TAB-001", "quantite": 100}]}
409 · POST /api/reservations : stock insuffisant (rien n'est réservé)
{"erreur": "stock_insuffisant", "message": "Stock insuffisant : aucune réservation n'a été enregistrée.",
 "details": {"lignes": [{"code_article": "TAB-004", "demande": 61, "disponible": 60, "suffisant": false}]}}
409 · POST /api/commandes : référence déjà utilisée
{"erreur": "commande_existante", "message": "Une commande avec la référence client « WEB-2026-0002 » existe déjà.",
 "details": {"reference_client": "WEB-2026-0002"}}
422 · données invalides
{"erreur": "validation_echouee", "message": "Les données envoyées sont invalides.",
 "details": {"champs": {"lignes.0.unite": ["La valeur du champ lignes.0.unite est invalide (valeurs acceptées : unite, carton)."]}}}
503 · panne simulée (en-tête Retry-After: 30)
{"erreur": "service_indisponible", "message": "Panne simulée : le service est indisponible pendant encore 30 seconde(s).",
 "details": {"secondes_restantes": 30}}
201 · POST /api/ordres-fabrication et POST /api/boutique/commandes/{id}/statut (200)
{"id": 7, "code_article": "TAB-004", "quantite": 1, "reference_commande": "WEB-2026-0004", "statut": "planifie"}

Exercices (à rendre, par équipe)

  1. Traduire. Écrivez le corps JSON du POST /api/commandes correspondant à la commande WEB-2026-0001 (sku, conditionnement, date, noms de champs). Quelles transformations avez-vous dû faire ?
  2. Prévoir. Complétez ce tableau pour les 8 commandes, traitées dans l'ordre : appels à faire (dans l'ordre), réponse attendue de chacun, quantités réservées en unités, et statut renvoyé à la boutique. Indiquez le stock disponible de chaque article touché après chaque commande.
    CommandeAppels dans l'ordreRéponses attenduesStock aprèsStatut renvoyé
    WEB-2026-0001    
    WEB-2026-0002    
    WEB-2026-0003    
    WEB-2026-0004    
    WEB-2026-0005    
    WEB-2026-0006    
    WEB-2026-0002    
    WEB-2026-0008    
  3. Dessiner. Représentez votre workflow : la liste des nœuds n8n (déclencheur, HTTP Request, Split Out, Code, IF…), leur ordre, et pour chaque appel de l'ERP ce que vous faites des réponses 201, 409, 422 et 503. Où renvoyez-vous le statut à la boutique ?
  4. Diagnostiquer. Pour chaque situation, donnez la cause probable et la correction :
    • un appel rĂ©pond 401 non_authentifie alors que la clĂ© semble correcte ;
    • la crĂ©ation d'une commande rĂ©pond 422 avec lignes.0.unite invalide pour une ligne « piece » de la boutique ;
    • la commande WEB-2026-0002 rĂ©pond 409 commande_existante au deuxième passage de votre workflow : est-ce un bug ? que faire ?
    • tous les appels de l'ERP rĂ©pondent 503 avec Retry-After: 30 alors que la boutique rĂ©pond normalement.
  5. Réfléchir. L'ERP tombe en panne entre la création de la commande et sa réservation. Que se passe-t-il au passage suivant de votre workflow ? Comment éviter de perdre ou de dupliquer une commande ? Pourquoi ne faut-il surtout pas envoyer « 5 » (cartons) dans la réservation de la commande WEB-2026-0002 ?

6. Travail demandé

Un workflow (ou plusieurs) qui traite toutes les commandes de la boutique. Les 8 commandes ne se ressemblent pas : chacune cache un cas à gérer.

a. Récupérer les commandes à traiter

  • Lisez GET /api/boutique/commandes?statut=nouvelle. Une commande dĂ©jĂ  traitĂ©e n'y figure plus.
  • Les commandes arrivent progressivement : faites tourner le workflow Ă  intervalle rĂ©gulier (Schedule Trigger) ou relancez-le.

b. Traduire et enregistrer dans l'ERP

  • Convertissez le format (voir plus haut) puis envoyez la commande avec POST /api/commandes. Les quantitĂ©s en carton sont converties par l'ERP.
  • Une mĂŞme reference_client ne peut ĂŞtre utilisĂ©e qu'une fois : la boutique peut renvoyer une commande. Traitez le 409 sans rĂ©server deux fois.
  • Un 422 signale des donnĂ©es invalides : lisez details.

c. Contrôler le stock et réserver

  • La rĂ©servation se fait en unitĂ©s : POST /api/reservations. Elle est « tout ou rien » : un 409 stock_insuffisant dĂ©taille les lignes en cause.
  • Une quantitĂ© exactement Ă©gale au disponible est acceptĂ©e ; une unitĂ© de plus est refusĂ©e.

d. Fabriquer ou prévenir

  • Stock insuffisant : crĂ©ez un ordre avec POST /api/ordres-fabrication pour la quantitĂ© manquante.
  • Informez le bon service avec POST /api/notifications (adv, production ou client) : refus, rupture, anomalie.

e. Renvoyer le résultat à la boutique

  • POST /api/boutique/commandes/{id}/statut avec acceptee, refusee ou en_attente_fabrication (et un motif pour un refus).
  • Le tableau de l'onglet Boutique de votre espace Ă©quipe doit reflĂ©ter ces statuts.

f. Rendre le workflow robuste

  • Une panne de l'ERP peut ĂŞtre dĂ©clenchĂ©e sur votre Ă©quipe (par vous avec POST /api/panne, ou par l'enseignant) : l'ERP rĂ©pond 503 avec Retry-After. La boutique, elle, continue de rĂ©pondre. Configurez les nouvelles tentatives (Retry On Fail) et une branche d'erreur : aucune commande ne doit ĂŞtre perdue.
  • La limite est de 120 requĂŞtes par minute : au-delĂ , 429. Évitez les boucles qui martèlent l'API.
  • Un 401 signale une clĂ© absente ou erronĂ©e.

7. Outils pour vous aider

  • Journal (espace Ă©quipe) : tous vos appels avec requĂŞte et rĂ©ponse, pour comprendre ce qui est rĂ©ellement parti.
  • Essayer un appel (espace Ă©quipe) : tester un appel sans n8n.
  • Boutique, Stocks, Commandes, RĂ©servations, Ordres, Notifications : l'Ă©tat de vos donnĂ©es après chaque test.
  • Les donnĂ©es se remettent Ă  zĂ©ro Ă  la demande de l'enseignant (la boutique recommence alors ses publications).

8. À rendre

  • Le document de la phase 0 (rĂ©ponses aux 5 exercices).
  • L'export JSON de vos workflows n8n.
  • Une capture de l'onglet Boutique avec les statuts des 8 commandes, et un extrait du journal montrant un 409 et une panne (503) gĂ©rĂ©s.
  • Un court compte rendu : choix faits (traduction du format, conversion des cartons, gestion des erreurs) et difficultĂ©s rencontrĂ©es.

Les modalités exactes (date, format, barème) sont précisées par l'enseignant.

9. Règles à respecter

  • Ne saisissez aucune donnĂ©e personnelle ni aucun mot de passe rĂ©el dans vos workflows : tout est fictif ici.
  • Ne partagez pas votre clĂ© d'Ă©quipe ; chaque Ă©quipe n'utilise que la sienne.
  • Une exĂ©cution est coupĂ©e après 5 minutes. Évitez les boucles infinies.
  • Certains nodes sont dĂ©sactivĂ©s (commandes système, fichiers, nodes communautaires, API publique). C'est voulu.

10. En cas de problème

SymptĂ´meQue faire
401 non_authentifieVérifiez le credential : nom Authorization, valeur Bearer suivi de la clé, sans espace en trop.
« Connection refused » ou délai dépasséVérifiez l'URL (https://cacaomatic.alt4b.fr/api/…), puis testez /api/ping dans le navigateur.
La liste de la boutique est incomplèteNormal : la boutique publie ses commandes au fil du temps (a_venir). Relancez plus tard ou demandez à l'enseignant de tout libérer.
422Le corps JSON est incomplet ou mal typé : lisez details dans la réponse.
503Panne simulée de l'ERP sur votre équipe : attendez la durée de Retry-After (visible dans l'espace équipe) ou gérez-la dans le workflow.
429Plus de 120 requêtes par minute : attendez une minute et vérifiez qu'une boucle ne tourne pas.
Workflow coupéIl a dépassé 5 minutes : simplifiez-le.
Autre erreurFaites une capture d'écran du message (et du Journal) et appelez l'enseignant.

Bonus · Ajouter une IA locale

Facultatif, une fois le travail principal terminé : faites classer ou résumer par un modèle local les messages que vos workflows envoient.

Credential Ollama (une seule fois)

  1. Credentials → Create credential → cherchez Ollama.
  2. Base URL : http://ollama:11434 (sans espace ni « / » final).
  3. Save : le message « Connection tested successfully » doit s'afficher. Nommez-le par exemple « Ollama équipe X ».

Un assistant qui répond

  1. Nouveau workflow → On chat message → + → Basic LLM Chain.
  2. Sous la chaîne, + de Model → Ollama Chat Model : credential ci-dessus, Model = gemma4:e4b-it-q4_K_M.
  3. Open chat, écrivez un message et vérifiez la réponse.

Exemple de consigne : classer une notification

Classe ce message dans une seule rubrique parmi : adv, production, client.
Réponds uniquement par le nom de la rubrique.

Message : {{ $json.chatInput }}

Idée : utilisez la rubrique renvoyée comme destinataire de POST /api/notifications.

Utilisez uniquement le modèle gemma4:e4b-it-q4_K_M. Tous les groupes le partagent : si la réponse est lente, patientez avant de relancer. Symptômes : « Connection refused » → vérifiez l'URL ; modèle absent de la liste → sauvegardez le credential, rouvrez le node et rechargez la page.