TrackChantier API
Documentation d’intégration pour synchroniser vos données métiers (compagnies, agences, employés, chantiers).
Présentation

Intégrer TrackChantier à votre écosystème

L’API TrackChantier vous permet de pousser vos données depuis un ERP ou un outil externe vers la plateforme TrackChantier : compagnies, agences, postes, employés, chantiers et hiérarchie de phases/zones/étapes.

Base URL
env

Vous ciblez votre fonction HTTP Firebase :

BASE_URL = https://<region>-<project-id>.cloudfunctions.net

Dans la collection Postman fournie, cette valeur est injectée via la variable BASE_URL.

Ressources principales
/upsertCompagnies
Création / mise à jour de compagnies
/upsertAgences
Création / mise à jour d’agences
/upsertPostes
Référentiel de postes (CA, CT, CE, opérateur…)
/upsertEmployes
Création / mise à jour d’employés (avec lien agence + poste)
/upsertChantierFields
Création / mise à jour de chantiers (+ hiérarchie phases/zones/étapes)
Sécurité

Authentification & headers

Toutes les fonctions HTTP sont protégées par un secret TOKEN (stocké côté Firebase). Vous devez inclure le token dans chaque requête.

Headers requis
Authorization
Bearer <TOKEN> requis
Content-Type
application/json requis
x-odoo-token
Alternative pour certaines fonctions (upsertCompagnies) : x-odoo-token: <TOKEN> optionnel
curlcurl -X POST \
  "$BASE_URL/upsertCompagnies" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "id": "1", "nom": "TrackChantier" }'
Modèle de données

IDs en string & liens cohérents

L’API fonctionne en upsert : chaque appel crée le document s’il n’existe pas, ou fusionne les champs envoyés s’il existe déjà (merge Firestore).

Règles d’ID
  • Les IDs sont des numériques castés en chaîne : "1", "2"
  • Vous pouvez envoyer un number, il sera transformé en string côté API.
  • Les relations se font toujours via ces mêmes IDs string.
compagnies/{id}
ex : "1" pour TrackChantier
agences/{id}
lié à companyId (string)
postes/{id}
ex : "1" = Chargé d’affaires
employees/{id}
lié à agenceId + posteId
chantiers/{id}
ex : "1", et champs caId, ctId, ceId pointent vers employees/{id}.
Comportement “merge”

Sur la plupart des endpoints, seuls les champs envoyés sont écrasés. Les champs non présents dans le body sont laissés intacts.

  • Pour upsertChantierFields, beaucoup de champs sont conditionnés par un has('path') : si vous n’envoyez pas le champ, l’ancienne valeur est conservée.
  • L’adresse du chantier est mise à jour uniquement si vous envoyez au moins un des champs street, street2, zip, city, country.
  • Les phases/zones/étapes sont upsertées sans suppression des éléments existants non mentionnés.
Référentiel

Endpoints principaux

Voici les endpoints utilisés dans la collection “TrackChantiers – API INIT (TrackChantier)” pour créer une base de données de tests cohérente.

/upsertCompagnies
POST

Création / mise à jour de compagnies.

Body : objet ou tableau de compagnies
Auth : Bearer TOKEN
Nom du champ Obligatoire Type Description
idOuistring / numberIdentifiant de la compagnie, casté en string dans Firestore
nom / nameNonstringNom affiché de la compagnie
salarie_de_pretNonboolIndique si la compagnie gère du prêt de salariés
loan_idsNonarrayListe d’IDs externes (ex : contrats de prêt Odoo) conservée telle quelle
chantiersDePretNonarray<object>Sous-collection compagnies/{id}/chantiersDePret. Chaque élément : { companyId, chantierId } ou { company_id, chantier_id } (tous castés en string).
curlcurl -X POST "$BASE_URL/upsertCompagnies" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "1",
    "nom": "TrackChantier",
    "salarie_de_pret": false,
    "loan_ids": [],
    "chantiersDePret": [
      { "companyId": "1", "chantierId": "1" }
    ]
  }'
jsawait fetch(`${BASE_URL}/upsertCompagnies`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${TOKEN}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    id: "1",
    nom: "TrackChantier",
    salarie_de_pret: false,
    loan_ids: [],
    chantiersDePret: [
      { companyId: "1", chantierId: "1" }
    ]
  })
});
/upsertAgences
POST

Création / mise à jour d’agences. Supporte un objet unique ou un tableau d’agences.

Body : objet ou tableau d’agences
Auth : Bearer TOKEN
Nom du champ Obligatoire Type Description
idOuistring / numberID de l’agence, casté en string
nom / nameNonstringNom de l’agence
cityNonstringVille, stockée dans le champ ville
colorNonstringCouleur associée, stockée dans couleur
samsara_trackingNonboolActive le tracking Samsara pour l’agence
display_invoice_ratio_dashboardNonboolAffiche le ratio de facturation sur le dashboard
display_exploitation_dashboardNonboolAffiche le dashboard exploitation
warehouseNonstring / number / arrayID d’entrepôt Odoo (simple valeur ou premier élément d’un tableau), stocké dans warehouse_id
show_on_dashboardNonboolVisibilité de l’agence sur le dashboard principal (showOnDashboard)
show_on_dashboard_widgetNonboolVisibilité dans certains widgets du dashboard (showOnDashboardWidget)
show_on_missing_hours_widgetNonboolContrôle des heures manquantes / acomptes à valider (showWidgetAcomptesAValider)
show_widget_holidays_on_dashboardNonboolAffiche les congés sur le dashboard
show_widget_late_invoicesNonboolWidget factures en retard
show_widget_projects_to_invoiceNonboolWidget projets / situations à facturer (showWidgetSituationsMois)
show_widget_hours_trackingNonboolWidget pointages à contrôler (showWidgetPointagesAControler)
show_widget_agora_invoicesNonboolWidget factures AGORA
show_widget_acomptes_to_validateNonboolWidget acomptes à valider (showWidgetAcomptesToValidate)
margin_goalsNonanyObjectifs de marge, structure libre
goalsNonanyAutres objectifs agences, structure libre
companyIdNonstringID de la compagnie Firestore (compagnies/{companyId})
is_principalNonboolSi vrai, copie de l’agence enregistrée dans agences/GROUPE avec original_id et original_nom
exemple{
  "id": "1",
  "nom": "Agence Grenoble",
  "companyId": "1",
  "city": "Grenoble",
  "color": "#1976D2",
  "samsara_tracking": false,
  "display_invoice_ratio_dashboard": true,
  "display_exploitation_dashboard": true,
  "warehouse": 1,
  "show_on_dashboard": true,
  "show_on_dashboard_widget": true,
  "show_on_missing_hours_widget": false,
  "show_widget_holidays_on_dashboard": true,
  "show_widget_late_invoices": true,
  "show_widget_projects_to_invoice": true,
  "show_widget_hours_tracking": true,
  "show_widget_agora_invoices": false,
  "show_widget_acomptes_to_validate": false,
  "margin_goals": {},
  "goals": {},
  "is_principal": true
}
/upsertPostes
POST

Référentiel de postes utilisés par les employés.

Nom du champ Obligatoire Type Description
idOuistring / numberID du poste, casté en string
nomOuistringLibellé du poste
descriptionNonstringDescription métier
actifNonboolPoste actif ou non
exemple[
  { "id": "1", "nom": "Chargé d'affaires",      "actif": true },
  { "id": "2", "nom": "Conducteur de travaux",  "actif": true },
  { "id": "3", "nom": "Chef de chantier",       "actif": true },
  { "id": "4", "nom": "Opérateur de chantier",  "actif": true }
]
/upsertEmployes
POST

Création / mise à jour d’employés dans la collection employees. L’API vérifie notamment que agenceId et posteId existent.

Body : objet ou tableau d’employés
Auth : Bearer ODOO_TOKEN
Nom du champ Obligatoire Type Description
idOuistring / numberID de l’employé, casté en string
firstName / prenomOui (création)stringPrénom de l’employé
lastName / nomOui (création)stringNom de famille
initialNonstringInitiales (ex : FB)
displayNameNonstringNom complet d’affichage ; si absent il est calculé à partir de firstName + lastName
posteNonstringLibellé du poste (ex : Chargé d'affaires)
posteIdNonstring / numberID du poste, doit correspondre à postes/{posteId}
agenceNonstringNom de l’agence (ex : Agence Grenoble)
agenceIdNonstring / numberID d’agence, casté en string, référence agences/{agenceId}
companyNonstringNom de la compagnie (TrackChantier)
companyIdNonstringID de la compagnie (compagnies/{companyId})
parentIdNonstringID du manager / responsable hiérarchique
parentNameNonstringNom du manager
userIdNonstringID utilisateur TrackChantier associé
mailNonstringEmail professionnel, utilisé aussi pour les accès
isManagerNonboolIndique si l’employé est manager
telephoneNonstringTéléphone personnel
telephoneProNonstringTéléphone professionnel
telUrgenceNonstringNuméro à contacter en cas d’urgence
intitulePosteNonstringIntitulé de poste complet
matriculeNonstringMatricule interne
agoraMailEmployeeNonstringEmail AGORA de l’employé
agoraMailAgenceNonstringEmail AGORA de l’agence
agoraMailGroupeNonstringEmail AGORA du groupe
activeNonboolEmployé actif ou archivé
coachNonstringNom du coach
coachIdNonstringID du coach
formationDriveNonstringID / URL de dossier Drive lié aux formations
adresseNonobjectAdresse postale normalisée : { rue, rue1, rue2, cp, ville }
googleDriveNonobjectInformations Drive diverses, structure libre
skillsNonarrayCompétences / habilitations de l’employé
formationsNonobject / arrayFormations liées à l’employé
exemple[
  {
    "id": "1",
    "firstName": "Fabien",
    "lastName": "BEL",
    "poste": "Chargé d'affaires",
    "posteId": "1",
    "agence": "Agence Grenoble",
    "agenceId": "1",
    "company": "TrackChantier",
    "companyId": "1",
    "mail": "fabien@fbel.info",
    "active": true,
    "telephonePro": "+33 4 00 00 00 00",
    "adresse": {
      "rue1": "5 Rue des Écoles",
      "cp": "38210",
      "ville": "La Rivière"
    },
    "skills": [],
    "formations": {}
  }
]
/upsertChantierFields
POST

Endpoint principal pour créer / mettre à jour un chantier et ses métadonnées (agence, client, dates, objectifs, hiérarchie, etc.). La plupart des champs sont conditionnés par la présence de la clef dans le body (si vous ne l’envoyez pas, la valeur existante est conservée).

Body : objet chantier unique
Auth : Bearer TOKEN
Nom du champ Obligatoire Type Description
idOuistringID du chantier, casté en string
nameNonstringNom du chantier (stocké dans nom)
start_dateNonstring (YYYY-MM-DD)Date de démarrage
end_dateNonstring (YYYY-MM-DD)Date de fin
agenceNonstringNom de l’agence
agence_idNonstringID de l’agence → stocké dans agenceId
clientNonstringNom du client
client_idNonstringID externe du client
client_typeNonstringType du client (public / privé…)
main_companyNonstringCompagnie principale d’exploitation
companyNonstringNom de la compagnie (TrackChantier)
company_idNonstringID de la compagnie (stocké dans companyId)
company_detailsNonanyDétails supplémentaires sur la compagnie
maitre_ouvrageNonstringMaître d’ouvrage
maitre_oeuvreNonstringMaître d’œuvre
numberNonstringNuméro du chantier (ex: 25001)
statusNonstring Statut (valeurs: demarre, non_demarre, administratif, archive, termine)
odoo_projectNonstring / numberID projet Odoo
odoo_accountNonstring / numberID compte Odoo
sales_orderNonanyRéférence de commande de vente
id_drive_folderNonstringID du dossier Drive principal
driveNonobjectListe des sous-dossiers Drive (plans, photos…)
advancement_hoursNonnumberObjectif d’avancement global
target_hoursNonnumberObjectif total d’heures du chantier
times_setNonboolObjectifs par zone activés
is_closedNonboolChantier clos
date_closedNonstringDate de clôture
int_type_idNonstring / numberType d’intervention
location_idNonstring / numberLocalisation Odoo
caNonobjectChargé d’affaires → {id, name}
ctNonobjectConducteur de travaux → {id, name}
ceNonobjectChef de chantier → {id, name}
charge_da_idNonstringID CA compatible DOLO
conducteur_travaux_idNonstringID CT compatible DOLO
chef_chantier_idNonstringID CE compatible DOLO
streetNonstringRue
street2NonstringComplément de rue
zipNonstringCode postal
cityNonstringVille
countryNonstringPays
adresseNonobject Objet adresse consolidé : { rue, rue1, rue2, codePostal, departement, ville, country }
phasesNonarray Hiérarchie complète phases → zones → étapes
exemple{
  "id": "1",
  "name": "Réhabilitation école de La Rivière",
  "start_date": "2025-11-15",
  "end_date": "2026-03-31",
  "street": "5 Rue des Écoles",
  "zip": "38210",
  "city": "La Rivière",
  "country": "FR",
  "agence": "Agence Grenoble",
  "agence_id": "1",
  "client": "Ville de La Rivière",
  "client_id": "1",
  "client_type": "public",
  "company": "TrackChantier",
  "company_id": "1",
  "number": "25001",
  "status": "demarre",
  "odoo_project": 123,
  "odoo_account": 456,
  "id_drive_folder": "DRIVE_FOLDER_ID",
  "drive": {
    "plans": "FOLDER_ID_PLANS",
    "photos": "FOLDER_ID_PHOTOS"
  },
  "advancement_hours": 120,
  "target_hours": 200,
  "times_set": true,
  "is_closed": false,
  "ca": { "id": "1", "name": "Fabien BEL" },
  "ct": { "id": "2", "name": "Nicolas Dubost" },
  "ce": { "id": "3", "name": "Jonathan Paterne" },
  "phases": [
    {
      "id": 1,
      "name": "Phase 1",
      "order": 1,
      "start_date": "2025-11-15",
      "end_date": "2025-12-15",
      "zones": [
        {
          "id": 1,
          "name": "Zone A",
          "order": 1,
          "etapes": [
            { "uuid": "etp-1", "name": "Préparation", "planned_hours": 40 }
          ]
        }
      ]
    }
  ]
}
Scénario d’init

Créer une base de tests cohérente

La collection “TrackChantiers – API INIT (TrackChantier)” illustre un scénario complet avec une compagnie, plusieurs agences, postes, employés, et chantiers liés aux bons CA / CT / CE.

Ordre recommandé des appels
  1. /upsertPostes – créer vos postes (Chargé d’affaires, Conducteur de travaux, Chef de chantier, Opérateur…).
  2. /upsertCompagnies – créer la compagnie principale (ex : id = "1", nom = "TrackChantier").
  3. /upsertAgences – rattacher des agences à la compagnie (companyId = "1").
  4. /upsertEmployes – créer les employés en précisant posteId + agenceId + companyId.
  5. /upsertChantierFields – créer vos chantiers en pointant ca.id, ct.id, ce.id sur les employés créés à l’étape 4.
Exemples

Script minimal d’intégration

Exemple de script Node.js pour rejouer la logique de la collection d’init : création de la compagnie TrackChantier, des agences, des postes, des employés et d’un chantier lié.

nodeimport fetch from "node-fetch";

const BASE_URL = process.env.TRACKCHANTIERS_BASE_URL;
const TOKEN = process.env.TRACKCHANTIERS_TOKEN;

async function call(endpoint, payload) {
  const res = await fetch(`${BASE_URL}/${endpoint}`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${TOKEN}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(payload)
  });

  if (!res.ok) {
    const txt = await res.text();
    throw new Error(`${endpoint} → HTTP ${res.status}: ${txt}`);
  }
  return res.json().catch(() => ({}));
}

(async () => {
  // 1. Postes
  await call("upsertPostes", [
    { id: "1", nom: "Chargé d'affaires",     actif: true },
    { id: "2", nom: "Conducteur de travaux", actif: true },
    { id: "3", nom: "Chef de chantier",      actif: true },
    { id: "4", nom: "Opérateur de chantier", actif: true }
  ]);

  // 2. Compagnie
  await call("upsertCompagnies", { id: "1", nom: "TrackChantier" });

  // 3. Agences
  await call("upsertAgences", [
    { id: "1", nom: "Agence Grenoble", companyId: "1", ville: "Grenoble", codePostal: "38000" },
    { id: "2", nom: "Agence Lyon",     companyId: "1", ville: "Lyon",     codePostal: "69000" },
    { id: "3", nom: "Agence Paris",    companyId: "1", ville: "Paris",    codePostal: "75000" }
  ]);

  // 4. Employés (François, Julien, Joe)
  await call("upsertEmployes", [
    {
      id: "1",
      firstName: "François",
      lastName: "Biol",
      poste: "Chargé d'affaires",
      posteId: "1",
      agence: "Agence Grenoble",
      agenceId: "1",
      company: "TrackChantier",
      companyId: "1",
      mail: "François@gmail.info",
      active: true
    },
    {
      id: "2",
      firstName: "Julien",
      lastName: "Dubois",
      poste: "Conducteur de travaux",
      posteId: "2",
      agence: "Agence Lyon",
      agenceId: "2",
      company: "TrackChantier",
      companyId: "1",
      mail: "Julien@gmail.info",
      active: true
    },
    {
      id: "3",
      firstName: "Joe",
      lastName: "Pater",
      poste: "Chef de chantier",
      posteId: "3",
      agence: "Agence Paris",
      agenceId: "3",
      company: "TrackChantier",
      companyId: "1",
      mail: "Joe@gmail.info",
      active: true
    }
  ]);

  // 5. Chantier lié aux 3 encadrants
  await call("upsertChantierFields", {
    id: "1",
    name: "Réhabilitation école de La Rivière",
    start_date: "2025-11-15",
    end_date: "2026-03-31",
    street: "5 Rue des Écoles",
    zip: "38210",
    city: "La Rivière",
    country: "FR",
    agence: "Agence Grenoble",
    agence_id: "1",
    client: "Ville de La Rivière",
    client_id: "1",
    client_type: "public",
    company: "TrackChantier",
    company_id: "1",
    number: "25001",
    ca: { id: "1", name: "François Biol" },
    ct: { id: "2", name: "Julien Dubois" },
    ce: { id: "3", name: "Joe Pater" }
  });

  console.log("Base de tests TrackChantier initialisée ✅");
})().catch(err => {
  console.error(err);
  process.exit(1);
});
Postman

Télécharger la collection d’exemples

Vous pouvez utiliser une collection Postman prête à l’emploi pour rejouer tous les endpoints décrits dans cette documentation. Elle contient des requêtes d’init complètes pour créer une base de tests cohérente (compagnie, agences, postes, employés, chantiers…).

Collection Postman

Téléchargez le fichier suivant et importez-le dans Postman :

👉 Télécharger la collection TrackChantiers – API INIT

Important : après l’import, ouvrez l’onglet Variables de la collection et configurez :

  • TOKEN – le secret attendu par les endpoints (header Authorization: Bearer <TOKEN>).
  • BASE_URL – l’URL de base de vos fonctions HTTP Firebase (ex: https://europe-west1-votre-projet.cloudfunctions.net).

Une fois ces variables renseignées, vous pouvez lancer les requêtes dans l’ordre recommandé (section “Flow d’init”) pour initialiser votre environnement de tests.