fix(produits): une seule session Qlik pour la recherche — c'était la cause des code 15

La sync de la Grille marche parce qu'elle est SEULE sur le serveur Qlik. La
recherche, elle, ouvrait une seconde session concurrente pour les mesures
pendant que la première tenait une énorme sélection : l'Engine coupait les
requêtes (code 15 « Request aborted »).

- Les mesures réseau (CA, quantité, magasins, CA/magasin, marge %) sortent
  désormais du MÊME cube que l'identité, dans la même session : la fenêtre 12
  mois glissants est sélectionnée sur le champ Date, les master measures sont
  résolues par titre comme dans l'extraction de la Grille, et le cube est trié
  par quantité réseau décroissante. Plus d'appel à
  fetchNetworkMetricsPlaywright pendant une recherche.
- Sélection plafonnée à 300 libellés au lieu de 1 000 : sélectionner les 13 446
  libellés contenant « tapis » prenait 81 s puis 486 s. Le cube étant trié, 300
  suffisent pour remonter les meilleures ventes.
- Une seule recherche simultanée (deux jobs en parallèle aggravaient la
  saturation).
- Le détail mensuel vient du cache : le cube de recherche est agrégé. L'upsert
  passe les colonnes mensuelles en COALESCE pour qu'une recherche n'efface pas
  un mensuel déjà extrait par une sync.

Jointure catalogue : les 40 codes ne trouvaient aucune correspondance dans
articles.artcentrale (formats différents — « Article Code » renvoie 8 chiffres,
artcentrale en a 11). Le champ article_no_centrale est maintenant remonté comme
seconde clé candidate, la jointure essaie les deux, et un échantillon des deux
clés est loggué quand aucune ne joint.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfqUihgixw4K1AmJhizWiu
This commit is contained in:
Claude committed 2026-07-30 05:53:40 +00:00
1 parent c9ecb47cb1
commit b3a82cf1e8
4 files changed
+288 -99

No files matched your search

+22 -5
View File
@@ -30,11 +30,28 @@ ceux-là qu'on veut voir.
côté client, puis on sélectionne les valeurs retenues par numéro d'élément
(`SelectListObjectValues`). La sélection restreint la dimension « Article
Code » aux articles correspondants.
2. `fetchNetworkMetricsPlaywright()` — mesures réseau + détail mensuel sur la
fenêtre 12 mois glissants (mois en cours exclu), puis `upsertNetworkMetrics()`
pour que la fiche produit soit immédiatement servie depuis le cache.
3. `pgGetProduitsByCodeCentrale()` — rapprochement avec le catalogue Nancy.
Un code absent = produit réseau que nous ne référençons pas (`?cc=` sur la fiche).
2. Les **mesures réseau sortent du même cube**, dans la **même session Qlik** :
fenêtre 12 mois glissants sélectionnée sur le champ `Date`, master measures
résolues par titre, cube trié par quantité réseau décroissante (les meilleures
ventes en tête, donc pas besoin de lire tout l'ensemble sélectionné).
Elles sont ensuite persistées par `upsertNetworkMetrics()`.
⚠️ Ne **jamais** relancer `fetchNetworkMetricsPlaywright()` pendant une
recherche : deux sessions concurrentes sur la même app, dont une avec une
grosse sélection, et l'Engine coupe les requêtes (`code 15`). C'est ce qui
faisait échouer la recherche alors que la sync de la Grille — seule sur le
serveur — fonctionnait. Le détail mensuel vient donc du cache, et la fiche
produit garde son bouton « Actualiser depuis Qlik » pour l'extraire à la
demande sur un seul code.
3. `pgGetProduitsByCodeCentrale()` — rapprochement avec le catalogue Nancy, sur
**deux clés candidates** : la dimension « Article Code » et le champ
`article_no_centrale`, qui n'ont pas le même format sur l'app FF. Un code
absent = produit réseau que nous ne référençons pas (`?cc=` sur la fiche).
Plafonds mesurés en production : sélectionner les 13 446 libellés contenant
« tapis » prenait 81 s puis 486 s avant d'abandonner. La sélection est donc
plafonnée à **300 valeurs** (`MAX_VALEURS_SELECTION`) et une seule recherche
tourne à la fois.
L'API est **asynchrone** : `POST /api/produits/search?q=…` démarre un job et rend
la main tout de suite, `GET` renvoie l'avancement puis le résultat (polling client