diff --git a/src/app/api/diag/prix-vente/route.ts b/src/app/api/diag/prix-vente/route.ts index b8dc6cf..fee28c5 100644 --- a/src/app/api/diag/prix-vente/route.ts +++ b/src/app/api/diag/prix-vente/route.ts @@ -111,6 +111,26 @@ export async function GET(req: NextRequest) { } } + // ── 2 ter. Les valeurs de `site` telles qu'elles sont stockées ────────── + // Ces chaînes servent de CLÉ de recherche par magasin côté Grille : un + // espace de remplissage ou un code inattendu et le prix par magasin serait + // introuvable, sans que rien ne le signale à l'écran. + try { + const r = await db.execute(sql` + SELECT site, LENGTH(site)::int AS longueur, COUNT(*)::int AS lignes + FROM cube_pv + GROUP BY site, LENGTH(site) + ORDER BY lignes DESC + LIMIT 12 + `); + diagnostic.sites_cube_pv = { + commentaire: "la Grille recherche les codes magasin tels quels — longueur 3 attendue pour « 292 » / « 579 »", + valeurs: r.rows, + }; + } catch (e) { + diagnostic.sites_cube_pv = { erreur: (e as Error).message?.slice(0, 200) }; + } + // ── 3. Colonnes candidates du schéma, et leur remplissage réel ────────── // C'est la réponse à « le prix est peut-être ailleurs » : une colonne bien // nommée mais vide ne sert à rien, seul le comptage tranche. diff --git a/src/features/grid/api/get-product-rows.ts b/src/features/grid/api/get-product-rows.ts index a87d0cb..b59f4fc 100644 --- a/src/features/grid/api/get-product-rows.ts +++ b/src/features/grid/api/get-product-rows.ts @@ -394,7 +394,8 @@ async function buildProductRows(input: GetProductRowsInput): Promise = {}; for (const s of stockMap.get(product.codein) ?? []) { const pv = Number(s.pv); - if (s.site && Number.isFinite(pv) && pv > 0) depuisStock[s.site] = pv; + const site = String(s.site ?? "").trim(); + if (site && Number.isFinite(pv) && pv > 0) depuisStock[site] = pv; } if (Object.keys(depuisStock).length > 0) product.prixVenteByStore = depuisStock; } diff --git a/src/lib/pg-ff-client.ts b/src/lib/pg-ff-client.ts index fbdeefd..8b1580a 100644 --- a/src/lib/pg-ff-client.ts +++ b/src/lib/pg-ff-client.ts @@ -450,10 +450,12 @@ function buildNomMap(rows: PgNomRow[]): Map { * 1 requête SQL — remplace la partie stock du référentiel. */ export async function pgGetStockByFournisseur(codefou: string): Promise> { + // `site` est élagué pour la même raison que dans pgGetPrixVenteByFournisseur : + // il sert de clé de recherche (derniereLivraisonByStore, prix de repli). const result = await pgNoParallel(sql` SELECT a.codein, - cs.site, + TRIM(cs.site::text) AS site, cs.stockdispo::float, cs.qte::float, cs.valstock::float, @@ -487,10 +489,15 @@ export async function pgGetStockByFournisseur(codefou: string): Promise>> { + // Le site sert de CLÉ de recherche côté Grille (prixVenteByStore["292"]), d'où + // l'élagage : la synchronisation Apiflow écrit `cube_pv.site` brut, sans passer + // par le `safeStr` qu'elle applique à `cube_stock`. Une colonne MSSQL de largeur + // fixe remonterait complétée d'espaces, et la recherche échouerait en silence — + // le prix resterait visible en « tous magasins » et vide magasin par magasin. const result = await pgNoParallel(sql` SELECT a.codein, - cpv.site, + TRIM(cpv.site::text) AS site, cpv.pv::float AS pv FROM cube_pv cpv JOIN articles a ON a.no_id = cpv.artnoid