The code centrale is LaFoir'Fouille's 11-digit product identifier
(prefix 1000x). Only products referenced by the central purchasing
office have one: ~355 800 of 427 700 articles.
The column was already synced from SQL Server and returned by the
detail route via `a.*`, but was absent from the list route and could
not be filtered on.
- list route: select artcentrale, add `artcentrale` (partial match)
and `has_artcentrale=1|0` query params
- filter matches on prefix AND length 11, so malformed values with a
valid prefix ('10000', '10000192289P') are excluded
- COALESCE in the predicate: without it, NOT (NULL LIKE '1000%') is
NULL and the 61 442 NULL rows would vanish from has_artcentrale=0
- normalise '' to null in both routes so they agree
- sync: use safeStr so empty values are stored as NULL, not ''
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
37 KiB
Documentation API Foirfouille
Base URL : https://api.ffnancy.fr
Format : JSON
Méthode : GET uniquement (API lecture seule)
Données : Synchronisées depuis SQL Server chaque nuit à 6h45 (heure Paris)
Santé
GET /api/health
{ "status": "ok", "database": "foirfouille", "server": "postgres" }
Articles
Liste avec filtres
GET /api/articles
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
search |
string | Recherche dans libellé | — |
codein |
string | Code interne (partiel) | — |
ean |
string | EAN / GTIN | — |
codefou |
string | Code fournisseur (partiel) | — |
actif |
1/0 |
1 = actif, 0 = suspendu |
— |
artcentrale |
string | Code centrale (partiel) | — |
has_artcentrale |
1/0 |
1 = référencé centrale, 0 = non référencé |
— |
page |
int | Numéro de page | 1 |
limit |
int | Lignes par page (max 500) | 50 |
Code centrale (
artcentrale) : identifiant LaFoir'Fouille sur 11 chiffres, préfixe1000x(ex.10000137005). Seuls les produits référencés par la centrale en possèdent un — environ 355 800 articles sur 427 700. Pour les autres, le champ vautnull.has_artcentrale=1ne retient que les codes conformes : quelques valeurs parasites (10000,10000192289P) ont le bon préfixe mais pas la bonne longueur, et sont exclues.
Réponse :
{
"page": 1,
"limit": 50,
"articles": [
{
"no_id": 12345,
"codein": "ABC123",
"libelle1": "MON ARTICLE",
"libelle2": "SOUS-LIBELLE",
"lib_ticket": "MON ART",
"tax_code": "A",
"ach_code": "X",
"utilisable": "O",
"actif": "O",
"suspendu": null,
"suividatecreation": "2020-01-15T00:00:00.000Z",
"suividatemodif": "2024-06-01T00:00:00.000Z",
"artcentrale": "10000137005",
"prix_vente_mini": 15.00,
"prix_vente_maxi": 25.00,
"eco_ttc": 0.10,
"on_web": "O",
"interdit_remise": null,
"nomphoto": "ABC123.jpg",
"datedebvente": null,
"datefinvente": null,
"pa": 10.50,
"gtin": "3760000000001",
"codefou_principal": "FOU01",
"nom_fou_principal": "MON FOURNISSEUR",
"ref_fou_principale": "REF-FOU-001",
"pcb_principal": 6
}
]
}
Détail d'un article
GET /api/articles/:id
Paramètre : id = no_id de l'article
Réponse :
{
"article": { "no_id": 12345, "codein": "...", "libelle1": "...", "artcentrale": "10000137005", "pa": 10.50, "..." : "..." },
"gtins": [
{ "gtin": "3760000000001", "preferentiel": 1 }
],
"stock": [
{ "site": "001", "qte": 12, "prmp": 10.20, "valstock": 122.40, "pv": 18.50, "stockdispo": 10 }
],
"prix": [
{ "site": "001", "pv": 18.50 }
],
"fournisseurs": [
{
"codefou": "FOU01", "nom_fou": "MON FOURNISSEUR",
"reference": "REF-001", "ean13": "3760000000001",
"pcb": 6, "delai": 7, "preference": 1,
"prixachat": 10.50, "remise_promotion": 5.00
}
]
}
Fiche complète (référentiel)
GET /api/articles/:id/referentiel
Retourne toutes les informations consolidées de l'article :
{
"article": {
"no_id": 12345, "codein": "ABC123", "libelle1": "MON ARTICLE",
"prix_vente_mini": 15.00, "prix_vente_maxi": 25.00,
"eco_ht": 0.08, "eco_ttc": 0.10,
"on_web": "O", "pv_conseille": 19.90,
"nom_code": "RAY01", "nom_libelle": "RAYON MAISON", "nom_niveau": 1
},
"gtins": [ { "gtin": "3760000000001", "preferentiel": 1 } ],
"gammes": [
{ "gamme_code": "ETE2024", "gamme_libelle": "ÉTÉ 2024", "saison_code": "S24", "saison_libelle": "SAISON 2024" }
],
"stock": [
{ "site": "001", "qte": 12, "prmp": 10.20, "valstock": 122.40, "pv": 18.50, "stockdispo": 10 }
],
"stock_remballe": [],
"prix": {
"achat": 10.50,
"vente_par_site": [ { "site": "001", "pv": 18.50 } ]
},
"fournisseurs": [
{ "codefou": "FOU01", "nom_fou": "MON FOURNISSEUR", "ref_fou": "REF-001", "pcb": 6, "prixachat": 10.50 }
],
"performance": {
"derniere_entree": "2024-05-10T00:00:00.000Z",
"derniere_vente": "2024-06-15T00:00:00.000Z",
"qte_totale_vendue": 248,
"ca_ttc_total": 4588.00,
"marge_totale": 1020.50
},
"notes": []
}
Analyse mensuelle d'un article (ventes + stock mois par mois)
GET /api/articles/:id/mensuel?dateDebut=&dateFin=&site=
Retourne en une seule requête, mois par mois et par site :
- Ventes du mois (quantité, CA, marge)
- Stock fin de mois (dernier QteStock enregistré dans le mois)
- Réceptions du mois
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
dateDebut |
date | Date début YYYY-MM-DD |
2024-01-01 |
dateFin |
date | Date fin YYYY-MM-DD |
2099-12-31 |
site |
string | Filtrer par site | tous |
Réponse :
{
"artNoId": "12345",
"dateDebut": "2025-01-01",
"dateFin": "2025-12-31",
"data": [
{
"mois": "2025-12",
"site": "001",
"stock_fin_mois": 28,
"prmp_fin_mois": 10.20,
"ventes": {
"nb_passages": 18,
"qte_vendue": 17,
"ca_ht": 274.00,
"ca_ttc": 315.10,
"marge": 72.00,
"taux_marge": 22.86
},
"receptions": {
"nb_receptions": 1,
"qte_recue": 24
}
},
{
"mois": "2025-11",
"site": "001",
"stock_fin_mois": 45,
"prmp_fin_mois": 10.20,
"ventes": {
"nb_passages": 12,
"qte_vendue": 10,
"ca_ht": 161.00,
"ca_ttc": 185.15,
"marge": 42.00,
"taux_marge": 22.69
},
"receptions": null
}
]
}
stock_fin_mois= QteStock du dernier mouvement du mois (vente, réception ou autre). Si un mois n'a aucun mouvement il n'apparaît pas dans la réponse.ventesoureceptionssontnullsi aucun mouvement de ce type dans le mois.
Mouvements d'un article
GET /api/articles/:id/mouvements
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
dateDebut |
date | Date début YYYY-MM-DD |
2000-01-01 |
dateFin |
date | Date fin YYYY-MM-DD |
2099-12-31 |
site |
string | Filtrer par site (partiel) | — |
page |
int | Numéro de page | 1 |
limit |
int | Lignes par page (max 1000) | 100 |
Réponse :
{
"page": 1, "limit": 100,
"mouvements": [
{
"datmvt": "2024-06-15T00:00:00.000Z",
"site": "001",
"libmvt": "VENTE",
"genremvt": 3,
"qtemvt": 2,
"mntmvtttc": 37.00,
"margemvt": 8.20,
"qtestock": 10
}
]
}
Valeurs GenreMvt : 1 = entrée stock, 3 = vente, 4 = avoir/annulation, 9 = autre vente
Le CA TTC (
ca_ttc) représente les ventes brutes (mouvements négatifs des genremvt 3, 4, 9). Le champretours_ttccontient les retours séparément. CA net =ca_ttc - retours_ttc.
Dernier fournisseur par site
GET /api/articles/:id/dernier-fournisseur?site=
Retourne le fournisseur réel issu du dernier mouvement d'entrée en stock (GenreMvt = 1) pour chaque site. C'est le seul moyen fiable d'identifier quel fournisseur a livré l'exemplaire du produit actuellement en rayon.
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
site |
string | Filtrer par site (partiel) | tous |
Réponse :
{
"artnoid": "12345",
"fournisseurs_par_site": [
{
"site": "001",
"date_derniere_entree": "2026-05-15T00:00:00.000Z",
"codefou": "FOU01",
"nom_fournisseur": "MON FOURNISSEUR",
"qte_entree": 24,
"prmp": 10.20
}
]
}
Photo de produit (Proxy)
GET /api/articles/:id/photo?size=
Proxy et met en cache l'image du produit depuis lafoirfouille.fr en contournant les limitations de CORS.
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
size |
string | Taille de l'image (large pour 1200x1200H) |
300Wx300H |
Réponse : Le flux binaire de l'image avec un en-tête Content-Type approprié (ex: image/jpeg) et une directive de mise en cache HTTP.
Fournisseurs
Liste des fournisseurs
GET /api/fournisseurs?search=
| Paramètre | Type | Description |
|---|---|---|
search |
string | Recherche sur code/nom |
Réponse :
{
"count": 142,
"fournisseurs": [
{
"codefou": "FOU01",
"nom": "MON FOURNISSEUR SARL",
"adresse": "1 RUE DE LA PAIX",
"telephone": "0383000000",
"email": "contact@fournisseur.fr",
"nb_articles": 320,
"nb_actifs": 285,
"nb_suspendus": 35
}
]
}
Articles d'un fournisseur
GET /api/fournisseurs/:code/articles?page=&limit=
Réponse :
{
"code": "FOU01",
"page": 1, "limit": 50,
"articles": [
{
"no_id": 12345, "codein": "ABC123", "libelle1": "MON ARTICLE",
"ref_fou": "REF-FOU-001", "ean13": "3760000000001",
"pcb": 6, "delai": 7, "suspendu": null,
"prixachat": 10.50, "remise_promotion": 5.00,
"pa": 10.50, "pv_central": 18.50
}
]
}
Commandes en cours d'un fournisseur
GET /api/fournisseurs/:code/commandes
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
dateDebut |
date | Date début YYYY-MM-DD |
2020-01-01 |
dateFin |
date | Date fin YYYY-MM-DD |
2099-12-31 |
page |
int | Numéro de page | 1 |
limit |
int | Max 1000 | 100 |
Franco de port d'un fournisseur
GET /api/fournisseurs/:code/franco
Retourne le seuil de franco de port configuré pour le fournisseur (priorité à la table fouport2 via fouport par rapport à la table foucad).
Réponse :
{
"code": "FOU01",
"nom": "MON FOURNISSEUR SARL",
"franco_ht": 350.00,
"franco_port": 350.00,
"franco_general": 300.00,
"actif": "O",
"duree": 0,
"unite": null
}
Stock
Stock global
GET /api/stock?site=&page=&limit=
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
site |
string | Filtrer par site | tous |
page |
int | Numéro de page | 1 |
limit |
int | Max 1000 | 100 |
Stock d'un article (tous sites)
GET /api/stock/article/:id
Réponse :
{
"artnoid": 12345,
"stock": [
{
"site": "001",
"qte": 12,
"prmp": 10.20,
"valstock": 122.40,
"pv": 18.50,
"stockdispo": 10,
"stockmort": 0,
"stockcolis": 2,
"dernierevente": "2024-06-15T00:00:00.000Z",
"dernierereception": "2024-05-10T00:00:00.000Z",
"nbjoursdernierevente": 5,
"interditachat": null,
"codefou": "FOU01"
}
]
}
Stock d'un article à une date précise
GET /api/stock/article/:id/historique?date=&site=
Retourne le stock tel qu'il était à une date donnée, en utilisant le champ QteStock
enregistré dans chaque mouvement (valeur du stock au moment exact du mouvement).
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
date |
date | Date cible YYYY-MM-DD |
aujourd'hui |
site |
string | Filtrer par site (partiel) | tous |
Réponse :
{
"artNoId": "12345",
"date": "2025-12-31",
"stock": [
{
"site": "001",
"qte": 28,
"prmp": 10.20,
"date_dernier_mouvement": "2025-12-29T00:00:00.000Z",
"libelle_dernier_mouvement": "VENTE"
}
]
}
Principe : on prend le dernier mouvement enregistré avant ou à la date demandée. Si aucun mouvement n'existe avant cette date, le site ne figure pas dans la réponse.
Stock + ventes d'un article sur une période
GET /api/stock/article/:id/periode?dateDebut=&dateFin=&site=
Endpoint principal pour analyser un article sur une période passée. Retourne en une seule requête : stock de début, stock de fin, ventes et réceptions.
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
dateDebut |
date | Date début YYYY-MM-DD |
1er du mois |
dateFin |
date | Date fin YYYY-MM-DD |
aujourd'hui |
site |
string | Filtrer par site (partiel) | tous |
Réponse :
{
"artNoId": "12345",
"dateDebut": "2025-12-01",
"dateFin": "2025-12-31",
"data": [
{
"site": "001",
"stock_debut": {
"qte": 45,
"prmp": 10.20,
"au": "2025-11-30T00:00:00.000Z"
},
"stock_fin": {
"qte": 28,
"prmp": 10.20,
"au": "2025-12-29T00:00:00.000Z"
},
"ventes": {
"site": "001",
"nb_passages": 18,
"qte_vendue": 17,
"ca_ht": 274.00,
"ca_ttc": 315.10,
"marge": 72.00,
"taux_marge": 22.86,
"premiere_vente": "2025-12-02T00:00:00.000Z",
"derniere_vente": "2025-12-29T00:00:00.000Z"
},
"receptions": {
"site": "001",
"nb_receptions": 1,
"qte_recue": 0
}
}
]
}
Lecture : stock_debut = dernier mouvement avant dateDebut / stock_fin = dernier mouvement à ou avant dateFin. Les champs
ventesetreceptionssontnullsi aucun mouvement sur la période.
Stock d'un site (tous articles)
GET /api/stock/site/:site?page=&limit=
Valorisation du stock par site
GET /api/stock/valorisation
Réponse :
{
"valorisation": [
{
"site": "001",
"nb_articles": 4200,
"qte_totale": 85000,
"val_stock": 420000.00,
"val_pv_theorique": 890000.00,
"prmp_moyen": 12.50
}
]
}
Commandes
Commandes en cours
GET /api/commandes
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
dateDebut |
date | Date début YYYY-MM-DD |
2020-01-01 |
dateFin |
date | Date fin YYYY-MM-DD |
2099-12-31 |
codefou |
string | Filtrer par fournisseur | — |
page |
int | Numéro de page | 1 |
limit |
int | Max 1000 | 100 |
Réponse :
{
"page": 1, "limit": 100,
"commandes": [
{
"no_commande": 98765,
"codefou": "FOU01",
"codein": "ABC123",
"libelle": "MON ARTICLE",
"ref_fou": "REF-001",
"qte_cde": 24,
"prix_brut": 12.00,
"remise": 5.00,
"prix_net": 11.40,
"montant": 273.60,
"qte_acceptee": 24,
"qte_annulee": 0,
"qte_attente": 0,
"qte_reliquat": 0,
"date_livraison_cible": "2024-07-01T00:00:00.000Z",
"date_commande": "2024-06-01T00:00:00.000Z"
}
]
}
Détail d'une commande
GET /api/commandes/:noCommande
Réponse :
{
"noCommande": "98765",
"nbLignes": 15,
"montantTotal": 4250.80,
"lignes": [ { "..." : "..." } ]
}
Liste des réceptions
GET /api/commandes/receptions/liste
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
dateDebut |
date | Date début YYYY-MM-DD |
2020-01-01 |
dateFin |
date | Date fin YYYY-MM-DD |
2099-12-31 |
page |
int | Numéro de page | 1 |
limit |
int | Max 1000 | 100 |
Mouvements
Types de mouvements disponibles
GET /api/mouvements/types
Retourne tous les codes GenreMvt et libellés présents dans la base, avec leur nombre d'occurrences.
À appeler en premier pour connaître les codes de régularisation propres à votre magasin.
{
"types": [
{ "genremvt": 1, "libmvt": "RECEPTION", "nb_occurrences": "142580" },
{ "genremvt": 3, "libmvt": "VENTE", "nb_occurrences": "2130137" },
{ "genremvt": 2, "libmvt": "RETOUR CLIENT", "nb_occurrences": "8420" },
{ "genremvt": 5, "libmvt": "REGULARISATION", "nb_occurrences": "3210" },
{ "genremvt": 6, "libmvt": "INVENTAIRE", "nb_occurrences": "1850" }
]
}
Les codes exacts dépendent de votre paramétrage Mercalys. Consultez cet endpoint pour connaître la liste réelle.
Mouvements articles (tous types)
GET /api/mouvements/articles
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
dateDebut |
date | Date début YYYY-MM-DD |
2024-01-01 |
dateFin |
date | Date fin YYYY-MM-DD |
2099-12-31 |
site |
string | Filtrer par site | — |
codefou |
string | Filtrer par fournisseur | — |
genremvt |
int | Filtrer par type (ex: 1, 3, 5...) |
tous |
page |
int | Numéro de page | 1 |
limit |
int | Max 1000 | 100 |
Types principaux GenreMvt :
| Code | Type |
|---|---|
1 |
Entrée stock |
3 |
Vente |
| autres | Régularisations (voir /api/mouvements/types) |
Entrées en stock
GET /api/mouvements/entrees
Toutes les réceptions / entrées en stock (GenreMvt = 1).
Inclut la date de création de l'article pour retrouver la première entrée.
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
dateDebut |
date | Date début YYYY-MM-DD |
2024-01-01 |
dateFin |
date | Date fin YYYY-MM-DD |
2099-12-31 |
site |
string | Filtrer par site | — |
artNoId |
int | Filtrer sur un article précis | — |
page |
int | Numéro de page | 1 |
limit |
int | Max 1000 | 100 |
Réponse :
{
"page": 1, "limit": 100,
"entrees": [
{
"date_entree": "2025-12-10T00:00:00.000Z",
"site": "001",
"artnoid": 12345,
"codein": "ABC123",
"libelle1": "MON ARTICLE",
"date_creation_article": "2020-03-15T00:00:00.000Z",
"libelle_mouvement": "RECEPTION",
"qte_entree": 24,
"prmp": 10.20,
"stock_apres_entree": 36,
"codefou": "FOU01"
}
]
}
Pour trouver la première entrée d'un article : filtrez par
artNoIdavecdateDebut=2000-01-01et triez par date croissante (la première ligne = première réception).
Régularisations de stock
GET /api/mouvements/regularisations
Tous les mouvements hors ventes et hors réceptions : inventaires, corrections, démarques, transferts, retours, etc.
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
dateDebut |
date | Date début YYYY-MM-DD |
2024-01-01 |
dateFin |
date | Date fin YYYY-MM-DD |
2099-12-31 |
site |
string | Filtrer par site | — |
artNoId |
int | Filtrer sur un article précis | — |
page |
int | Numéro de page | 1 |
limit |
int | Max 1000 | 100 |
Réponse :
{
"page": 1, "limit": 100,
"regularisations": [
{
"date_mouvement": "2025-12-05T00:00:00.000Z",
"site": "001",
"artnoid": 12345,
"codein": "ABC123",
"libelle1": "MON ARTICLE",
"genremvt": 6,
"libelle_mouvement": "INVENTAIRE",
"qte": -3,
"valeur": -30.60,
"stock_apres": 42,
"prmp": 10.20
}
]
}
Une
qtenégative = sortie de stock (correction à la baisse, démarque…). Uneqtepositive = entrée hors réception (correction à la hausse, retour…).
Synthèse par jour et site
GET /api/mouvements/synthese?dateDebut=&dateFin=&site=
Réponse :
{
"synthese": [
{
"jour": "2024-06-15",
"site": "001",
"genremvt": 3,
"nb_mvt": 152,
"qte_totale": 312,
"mnt_ht": 4200.00,
"mnt_ttc": 4830.00,
"marge": 1050.00
}
]
}
Règlements de caisse
GET /api/mouvements/reglements
Liste l'ensemble des règlements et paiements de tickets de caisse de la table MvtReg.
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
dateDebut |
date | Date début YYYY-MM-DD |
2024-01-01 |
dateFin |
date | Date fin YYYY-MM-DD |
2099-12-31 |
page |
int | Numéro de page | 1 |
limit |
int | Lignes max (max 1000) | 100 |
Réponse :
{
"page": 1,
"limit": 100,
"reglements": [
{
"datmvt": "2026-05-28T14:30:00.000Z",
"codtick": "TK123456",
"codcartecli": "CARD789",
"coddev": "EUR",
"mntreg": 45.90,
"mntregdev": 45.90,
"clientnom": "DUPONT JEAN",
"echeance": null,
"reference": null,
"typereg": "CB",
"suividatecreation": "2026-05-28T14:31:00.000Z"
}
]
}
Performance
Chiffre d'affaires
GET /api/performance/ca
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
dateDebut |
date | Date début YYYY-MM-DD |
2024-01-01 |
dateFin |
date | Date fin YYYY-MM-DD |
2099-12-31 |
site |
string | Filtrer par site | tous |
groupBy |
jour / mois |
Granularité | jour |
Réponse :
{
"groupBy": "mois",
"dateDebut": "2024-01-01",
"dateFin": "2024-12-31",
"site": "tous",
"ca": [
{
"periode": "2024-06",
"site": "001",
"nb_ventes": 4820,
"qte_vendue": 9640,
"ca_ht": 85200.00,
"ca_ttc": 97980.00,
"retours_ttc": 1250.00,
"marge": 21300.00,
"taux_marge": 21.74
}
]
}
Hit-parade articles
GET /api/performance/hitparade
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
dateDebut |
date | Date début YYYY-MM-DD |
2024-01-01 |
dateFin |
date | Date fin YYYY-MM-DD |
2099-12-31 |
site |
string | Filtrer par site | tous |
limit |
int | Nombre d'articles (max 500) | 50 |
groupBy |
ca / qte / marge |
Critère de classement | ca |
codefou |
string | Filtrer par code fournisseur | — |
Réponse :
{
"dateDebut": "2024-01-01",
"dateFin": "2099-12-31",
"site": "tous",
"codefou": null,
"classementPar": "ca",
"hitparade": [
{
"codein": "ABC123",
"libelle1": "MON ARTICLE",
"site": "001",
"code_fournisseur": "FOU01",
"fournisseur": "MON FOURNISSEUR",
"nb_passages": 320,
"qte_vendue": 640,
"ca_ht": 5760.00,
"ca_ttc": 6624.00,
"marge": 1440.00,
"taux_marge": 21.74,
"derniere_vente": "2024-06-15T00:00:00.000Z"
}
]
}
CA par fournisseur
GET /api/performance/ca/fournisseur?dateDebut=&dateFin=&site=
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
dateDebut |
date | Date début YYYY-MM-DD |
2024-01-01 |
dateFin |
date | Date fin YYYY-MM-DD |
2099-12-31 |
site |
string | Filtrer par site | tous |
Réponse :
{
"ca_par_fournisseur": [
{
"fournisseur": "DURACELL",
"code_fournisseur": "D034",
"nb_articles": 12,
"qte_vendue": 840,
"ca_ht": 4200.00,
"ca_ttc": 5040.00,
"marge": 1680.00,
"taux_marge": 40.00
}
]
}
La jointure se fait via
artfou1.preference = 1(fournisseur principal de l'article) →fouident.codepour le nom.
CA par nomenclature (rayon)
GET /api/performance/ca/nomenclature?dateDebut=&dateFin=&site=&niveau=1
| Paramètre | Type | Description |
|---|---|---|
niveau |
int | Niveau de nomenclature (1 = racine) |
Réponse :
{
"ca_par_nomenclature": [
{
"code_nomen": "RAY01",
"libelle": "RAYON MAISON",
"niveau": 1,
"nb_articles": 420,
"qte_vendue": 8400,
"ca_ht": 95000.00,
"ca_ttc": 109250.00,
"marge": 23750.00
}
]
}
CA par gamme
GET /api/performance/ca/gamme?dateDebut=&dateFin=&site=
Réponse :
{
"ca_par_gamme": [
{
"gamme": "ETE2024",
"libelle_gamme": "ÉTÉ 2024",
"nb_articles": 85,
"qte_vendue": 1700,
"ca_ttc": 28900.00,
"marge": 6450.00
}
]
}
Tableau de bord quotidien (Dashboard)
GET /api/performance/dashboard
Retourne une synthèse complète du jour demandé (par rapport au même jour à N-1) ainsi que les 3 Top 10 (CA, Quantité et Marge) par site.
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
date |
date | Date cible YYYY-MM-DD |
hier |
site |
string | Filtrer par site (facultatif) | tous |
Réponse :
{
"date": "2026-03-20",
"date_n1": "2025-03-20",
"sites": [
{
"site": "001",
"ca_ttc": 12450.00,
"ca_ttc_n1": 11800.00,
"evol_ca": "+5.5%",
"trafic": 420,
"trafic_n1": 410,
"evol_trafic": "+2.4%"
}
],
"top10_ca": [
{
"codein": "129632",
"libelle1": "ETUI 10 CARTES",
"site": "001",
"ca_ttc": 245.50,
"qte_vendue": 60,
"marge": 95.20
}
],
"top10_qte": [
{
"codein": "129632",
"libelle1": "ETUI 10 CARTES",
"site": "001",
"ca_ttc": 245.50,
"qte_vendue": 60,
"marge": 95.20
}
],
"top10_marge": [
{
"codein": "129632",
"libelle1": "ETUI 10 CARTES",
"site": "001",
"ca_ttc": 245.50,
"qte_vendue": 60,
"marge": 95.20
}
]
}
Publicités (Ecoulement)
Liste des publicités avec écoulement
GET /api/publicites
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
search |
string | Recherche dans l'intitulé ou le code | — |
site |
string | Filtrer par site | — |
dateDebut |
date | YYYY-MM-DD |
2000-01-01 |
dateFin |
date | YYYY-MM-DD |
2099-12-31 |
statut |
string | en_cours, passees, futures, ou toutes |
— |
page |
int | Numéro de page | 1 |
limit |
int | Lignes par page | 50 |
Historique des publicités (toutes les pubs)
GET /api/publicites/historique
Mêmes paramètres que /api/publicites (sauf site, page, limit). Retourne une liste complète incluant les publicités sans données d'écoulement.
Détail d'une publicité (synthèse par site + articles)
GET /api/publicites/:code?site=
Retourne la synthèse de la publicité par site ainsi que les données d'écoulement de chaque article.
Commandes Automatiques
Propositions de commandes automatiques
GET /api/commandes-auto?site=&codefou=
Retourne les propositions de commandes auto agrégées par site et fournisseur, avec comparaison au franco de port.
Détail des articles proposés pour un fournisseur
GET /api/commandes-auto/:codefou?site=
Retourne le résumé (franco, quantités, montant) et le détail par article des propositions.
Schéma de la Base de Données
Structure complète de la base
GET /api/schema
Retourne la structure complète de la base (tables et colonnes).
Liste des tables (avec nombre de lignes)
GET /api/schema/tables?search=
Top tables (par volume)
GET /api/schema/top?limit=
Colonnes d'une table spécifique
GET /api/schema/tables/:tableName
Ranking (classement réseau)
Recherche ranking
GET /api/ranking?gencod=&codein=&site=&foucentrale=&limit=
Retourne les classements réseau et magasin des articles. Les données proviennent des fichiers d'import centrale (TURBOCAR, AUXENCE, etc.).
| Paramètre | Type | Description | Défaut |
|---|---|---|---|
gencod |
string | Code EAN exact | — |
codein |
string | Code interne article | — |
site |
string | Filtrer par site (partiel) | tous |
foucentrale |
string | Filtrer par fournisseur (partiel) | tous |
limit |
int | Nombre max (max 500) | 50 |
Réponse :
{
"count": 3,
"ranking": [
{
"gencod": "3700536100805",
"site": "292",
"libelle": "ETUI 10 CARTES FIDELITE COUSU FANTASIA",
"foucentrale": "FFTURBOC",
"nomfoucentrale": "TURBOCAR",
"ranking_ca": 90,
"ranking_qte": 39,
"ranking_mag_ca": 0,
"ranking_mag_qte": 0,
"ranking_mag_marge": 0,
"pv_calcule": 3.89,
"pv_mag": 0,
"pv_cen": null,
"codefamille": "350402",
"libellefamille": "EQUIPEMENT INTERIEUR ET EXTERIEUR",
"date_maj": "2024-05-06T00:00:00.000Z",
"date_calcul_mag": "1901-01-01T00:00:00.000Z",
"art_no_id": "2920000106042",
"codein": "129632"
}
]
}
Clés ranking :
| Champ | Description |
|---|---|
ranking_ca |
Classement réseau par chiffre d'affaires (1 = meilleur) |
ranking_qte |
Classement réseau par quantité vendue |
ranking_mag_ca |
Classement magasin par CA |
ranking_mag_qte |
Classement magasin par quantité |
ranking_mag_marge |
Classement magasin par marge |
pv_calcule |
Prix de vente calculé par la centrale |
pv_mag |
Prix de vente magasin |
pv_cen |
Prix de vente centrale |
Le ranking est lié aux articles via le GENCOD (EAN). La jointure avec
art_gtinpermet de retrouver leno_idet lecodein.
Ranking d'un article
GET /api/ranking/article/:id
Paramètre : id = no_id de l'article
Retourne le ranking pour tous les sites où l'article est référencé.
{
"artNoId": "2920000106042",
"ranking": [
{
"gencod": "3700536100805",
"site": "292",
"ranking_ca": 90,
"ranking_qte": 39,
"ranking_mag_ca": 0,
"ranking_mag_qte": 0,
"ranking_mag_marge": 0,
"foucentrale": "FFTURBOC",
"nomfoucentrale": "TURBOCAR",
"pv_calcule": 3.89,
"pv_mag": 0,
"pv_cen": null,
"codefamille": "350402",
"libellefamille": "EQUIPEMENT INTERIEUR ET EXTERIEUR",
"date_maj": "2024-05-06T00:00:00.000Z",
"date_calcul_mag": "1901-01-01T00:00:00.000Z"
}
]
}
Le ranking est aussi inclus dans la fiche référentiel :
GET /api/articles/:id/referentiel→ champranking.
Synchronisation
État de la sync
GET /api/sync/status
Réponse :
{
"sync": [
{
"table_name": "articles",
"last_sync": "2026-03-18T03:00:00.000Z",
"rows_synced": 42062,
"status": "ok",
"error_msg": null
}
]
}
Tables synchronisées :
| Table | Mode | Fréquence |
|---|---|---|
articles |
Delta (SUIVIDATEMODIF) | Nuit |
article_infosup |
Delta | Nuit |
art_gtin |
Upsert complet | Nuit |
artfou1 |
Delta | Nuit |
artfou2 |
Upsert complet | Nuit |
fouadr1 |
Refresh complet | Nuit |
cube_stock |
Refresh complet | Nuit |
cube_pa |
Refresh complet | Nuit |
cube_pv |
Refresh complet | Nuit |
mvtart |
Append par NO_ID (PK SQL Server) | Nuit |
mvtreg |
Append par date | Nuit |
nomenclature |
Refresh complet | Nuit |
gammes |
Refresh complet | Nuit |
art_gamme_saison |
Refresh complet | Nuit |
saisons |
Refresh complet | Nuit |
fouident |
Refresh complet | Nuit |
cdefou_vivant |
Refresh complet | Nuit |
cdefou_reception |
Delta | Nuit |
cdefou_receplig |
Delta | Nuit |
commande_fou |
Upsert (NO_ID) | Nuit |
cdefou_ligne |
Upsert (NO_ID) | Nuit |
commande_auto_qtepropo |
Refresh complet | Nuit |
plan_reappro |
Refresh complet | Nuit |
ranking |
Refresh complet | Nuit |
stat_dispoperm |
Upsert | Nuit |
phenix_quantite_conseille |
Refresh complet | Nuit |
statopca |
Upsert (site+date) | Nuit |
Photos (Indexation)
Re-indexation des photos
POST /api/photos/reindex
Déclenche de façon asynchrone l'analyse des sitemaps produits de lafoirfouille.fr pour extraire et associer les URLs des fiches produits aux codes photos dans la table ff_photo_index.
Réponse :
{
"indexed": 14258,
"elapsed_s": 32.5
}
Statut de l'indexation
GET /api/photos/status
Retourne le nombre total de photos indexées, la date de la dernière indexation, et si une indexation est actuellement en cours.
Réponse :
{
"total": 14258,
"last_index": "2026-05-28T04:15:30.000Z",
"indexing_in_progress": false
}
Exemples d'intégration JavaScript
const API_BASE = 'https://api.ffnancy.fr';
// Rechercher des articles
async function rechercherArticles(search, page = 1) {
const params = new URLSearchParams({ search, page, limit: 50 });
const res = await fetch(`${API_BASE}/api/articles?${params}`);
return res.json();
}
// Fiche complète d'un article
async function getFicheArticle(id) {
const res = await fetch(`${API_BASE}/api/articles/${id}/referentiel`);
return res.json();
}
// CA du mois en cours par jour
async function getCaMoisEnCours() {
const now = new Date();
const debut = new Date(now.getFullYear(), now.getMonth(), 1).toISOString().slice(0, 10);
const fin = new Date(now.getFullYear(), now.getMonth() + 1, 0).toISOString().slice(0, 10);
const params = new URLSearchParams({ dateDebut: debut, dateFin: fin, groupBy: 'jour' });
const res = await fetch(`${API_BASE}/api/performance/ca?${params}`);
return res.json();
}
// Top 10 articles par CA sur 30 jours
async function getTop10() {
const fin = new Date().toISOString().slice(0, 10);
const debut = new Date(Date.now() - 30 * 86400000).toISOString().slice(0, 10);
const params = new URLSearchParams({ dateDebut: debut, dateFin: fin, limit: 10, groupBy: 'ca' });
const res = await fetch(`${API_BASE}/api/performance/hitparade?${params}`);
return res.json();
}
// Stock d'un article
async function getStock(artNoId) {
const res = await fetch(`${API_BASE}/api/stock/article/${artNoId}`);
return res.json();
}
// Valorisation globale du stock
async function getValorisationStock() {
const res = await fetch(`${API_BASE}/api/stock/valorisation`);
return res.json();
}
// Commandes en cours d'un fournisseur
async function getCommandesFournisseur(codeFou) {
const res = await fetch(`${API_BASE}/api/fournisseurs/${codeFou}/commandes`);
return res.json();
}
Codes d'erreur HTTP
| Code | Description |
|---|---|
200 |
Succès |
404 |
Ressource introuvable (article, commande…) |
500 |
Erreur serveur (détail dans le champ error) |
Format erreur :
{ "error": "Article introuvable" }
Notes importantes
- Les données sont en lecture seule — aucun endpoint d'écriture
- Les données sont décalées d'au plus 24h (sync automatique à 6h45 heure Paris)
- Les champs dates sont au format ISO 8601 UTC
- La pagination commence à
page=1 - Le champ
no_idest l'identifiant interne SQL Server de l'article - Les codes sites (
001,002…) correspondent aux différents points de vente