Commit Graph
5 Commits
Author SHA1 Message Date
Claude 18edda7167 feat(api): expose la tendance réseau calculée dans /api/v1
La colonne « Tendance » de la Grille était la seule information visible que
l'API ne fournissait pas : elle est calculée dans le navigateur par
computeNetworkTrend(), qui vivait dans un composant "use client" donc
inaccessible côté serveur. L'API livrait la série mensuelle brute et laissait
chaque appelant refaire la régression — au risque de diverger de l'affichage.

- Le calcul part dans src/lib/network-trend.ts (module pur, sans React) ;
  le composant le réexporte, aucun import existant ne change.
- enrichRows ajoute `trend` : { direction, pct, label, monthsUsed }, calculé
  sur la même série que la colonne de l'application.
- openapi.json : schéma Trend documenté, et il est dit explicitement que la
  liste des propriétés de Product n'est pas exhaustive — la réponse porte la
  ligne de grille entière.

Vérifié au passage que rien n'est perdu en chemin : upsertGridRows stocke le
ProductRow complet dans la colonne jsonb `payload` (les colonnes scalaires ne
servent qu'aux index), ne filtre que les lignes sans codein, et pickFields
renvoie tout tant que `fields` n'est pas précisé.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y26nRZxTR57K7h8yqsF675
2026-08-06 10:36:35 +00:00
Claude 3a825866d2 feat(api): /api/v1/grid calcule le fournisseur à la demande
L'API lisait uniquement la table grid_rows, écrite en effet de bord quand un
humain ouvre la page Grille. Un fournisseur jamais ouvert renvoyait donc
202 not_ready : une app ou un agent externe ne pouvait consulter que ce qui
avait déjà été parcouru dans l'interface, ce qui vide l'API de son intérêt.

/api/v1/grid déclenche désormais getProductRows() quand l'instantané manque.
Le calcul est déjà protégé en amont (cache 10 min + verrou anti-concurrence)
et persiste l'instantané, donc seul le premier appel paie le coût ; les
suivants repassent par le chemin SQL rapide.

- compute=1 (défaut) : calcul à la demande, meta.computedOnDemand signale
  quand il a eu lieu. compute=0 : comportement strict d'avant.
- Fournisseur inconnu ou sans article : 404 explicite, distinct d'une panne
  (500) et de « pas encore calculé » (202).
- /api/v1/products/{codein} fait de même lorsque `fournisseur` est fourni,
  le calcul se faisant par lot fournisseur.
- openapi.json mis à jour : c'est ce document que lit un agent externe, il
  annonçait « aucun endpoint ne déclenche de recalcul ».

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y26nRZxTR57K7h8yqsF675
2026-08-06 10:29:26 +00:00
Claude 4b229acc9b feat(api): API prête pour une IA externe type ChatGPT
Adapte l'API CollectFlow aux exigences des Actions ChatGPT et garantit que
les données soient réellement disponibles pour un consommateur externe.

Schéma OpenAPI :
- openapi.json devient PUBLIC (sans clé). Il ne décrit que la structure, sans
  aucune donnée, or ChatGPT importe le schéma par URL avant que la clé ne soit
  configurée : l'exiger rendait l'import impossible.
- servers[0].url est désormais ABSOLUE (une URL relative est rejetée à
  l'import), construite depuis l'hôte appelant ou COLLECTFLOW_PUBLIC_URL.
- operationId sur chaque opération (requis par les Actions), schémas de
  réponse typés, et descriptions rédigées pour le modèle : quelle gamme
  utiliser pour raisonner, pourquoi préférer caParMagasinReseau au CA brut,
  que faire d'un 202 not_ready.

Disponibilité des données :
- Nouveau préchauffage /api/admin/grid-warmup + bouton dans Paramètres.
  Sans lui, l'API ne sert que les fournisseurs déjà ouverts à la main dans la
  Grille — une IA externe n'aurait presque rien vu. Le job calcule tous les
  fournisseurs séquentiellement (paralléliser saturerait PostgreSQL), saute
  ceux à jour depuis moins de 24 h et suit son avancement.

Documentation :
- Paramètres → marche à suivre pas à pas pour brancher un GPT (import du
  schéma, auth par clé personnalisée X-API-Key), et mention de
  COLLECTFLOW_PUBLIC_URL quand le domaine public diffère.

L'assistant interne et son API api.ffnancy.fr ne sont pas touchés.

Vérifié sur PostgreSQL local : schéma servi sans clé en 200, URL absolue,
5 operationId, auth apiKey/X-API-Key, surcharge COLLECTFLOW_PUBLIC_URL
effective, 5 endpoints en 200 avec la clé, 401 JSON sans clé, et recherche
transversale renvoyant bien plusieurs fournisseurs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y26nRZxTR57K7h8yqsF675
2026-08-05 10:21:31 +00:00
Claude a45e09777b feat(api): métriques Qlik + gamme serveur à jour, et doc de connexion dans Paramètres
L'instantané grid_rows fige la ligne au moment du calcul, mais deux données
évoluent indépendamment et doivent refléter l'état courant :

- Métriques réseau Qlik : qlik_network_metrics est alimentée par les syncs et
  les recherches réseau, hors du calcul de grille. Relues à chaque appel et
  exposées dans `network` (null quand le produit n'en a pas). Les colonnes
  caReseau/qteReseau/... de la ligne sont réalignées dessus.
- Gamme serveur : codeGamme peut être surchargée par un snapshot de session.
  L'API expose `codeGammeServeur`, la gamme NON modifiée telle qu'elle est en
  base PostgreSQL, relue à chaque appel. codeGammeInit est gardé aligné.

Ajout de pgGetGammesByCodeins() : variante sans jointure artfou1, nécessaire
car la recherche de l'API est transversale (pas de fournisseur connu).

Ce n'est pas un recalcul : deux lectures indexées bornées à la page courante
(500 lignes max). Mesuré à ~20 ms, soit le même coût que sans enrichissement.
Paramètre enrich=0 pour servir l'instantané brut.

Paramètres → nouvelle section « API CollectFlow — Connexion » : URL de base
déduite de l'origine, en-têtes d'authentification, liste des endpoints et des
paramètres, exemples curl copiables, lien vers openapi.json.

Vérifié sur PostgreSQL local avec un instantané volontairement périmé :
gamme snapshot A → serveur C, caReseau 111 → 990000, produit sans données
réseau → network null, produit sans gamme → codeGammeServeur null, enrich=0
redonnant bien les valeurs figées, et toujours zéro recalcul.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y26nRZxTR57K7h8yqsF675
2026-08-05 10:14:21 +00:00
Claude e79d7e51da feat(api): API CollectFlow /api/v1 — lecture de la grille et recherche
Les données de la grille (ventes 12 mois, stock, marges, gammes, métriques
réseau Qlik) n'étaient accessibles par aucun moyen programmatique, et
getProductRows() exige un fournisseur : chercher un produit sans le connaître
était impossible.

Contrainte de conception : l'API ne recalcule jamais rien. La grille était
reconstruite en direct et gardée seulement 10 min en mémoire ; une API qui
appellerait getProductRows() serait lente et imprévisible. On persiste donc
le résultat d'un calcul qui a déjà lieu, et on le sert.

- Table grid_rows : colonnes scalaires (filtre/tri/recherche en SQL) +
  payload jsonb du ProductRow complet. Remplie en effet de bord NON bloquant
  par getProductRows(), purge des articles disparus via computed_at. Survit
  aux redémarrages, contrairement au cache mémoire.
- Endpoints /api/v1 : fournisseurs, grid, products/search (transversale, tous
  fournisseurs), products/:codein, network/:codeCentrale, openapi.json.
  Pagination, tri sur liste blanche, projection de champs, validation zod.
  202 not_ready si un fournisseur n'a pas encore d'instantané.
- Authentification double : clé d'API (X-API-Key ou Bearer, SHA-256 en base,
  révocable) ou session existante. Le middleware exempte /api/v1 — sans quoi
  un script recevait une redirection 302 vers /login au lieu d'un 401 JSON.
- Gestion des clés dans /settings (server actions, clé affichée une seule fois).

Vérifié contre une base PostgreSQL locale : 401 JSON sans clé, 401 sur clé
révoquée, recherche renvoyant plusieurs fournisseurs, upsert + purge, et
24 appels /api/v1 sans déclencher un seul recalcul (l'ancienne route
/api/grid/rows en déclenche un à chaque appel).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y26nRZxTR57K7h8yqsF675
2026-08-05 10:14:21 +00:00