Prix de vente : élaguer le code magasin, qui sert de clé

Le code magasin de `cube_pv` sert de clé de recherche côté Grille
(`prixVenteByStore["292"]`). Or la synchronisation Apiflow l'écrit BRUT
(`site: r.Site`), là où elle fait passer celui de `cube_stock` par `safeStr`
— et aucune des deux ne l'élague. Une colonne MSSQL de largeur fixe remonte
complétée d'espaces : la recherche échouerait alors sans rien signaler, et
de la pire façon qui soit, le prix restant visible en « tous magasins » et
vide magasin par magasin.

`TRIM` des deux côtés, plus l'élagage du repli lu depuis le cube de stock.
L'opération ne coûte rien quand il n'y a pas de remplissage, et le doute
n'avait pas de raison de subsister.

La route de diagnostic affiche désormais les valeurs de `site` telles
qu'elles sont stockées, avec leur longueur : de quoi vérifier d'un coup
d'œil que les clés sont bien « 292 » et « 579 », sur trois caractères.

Commentaires SQL déplacés hors du littéral gabarit, comme partout ailleurs
dans le fichier : un accent grave dans un commentaire refermait la chaîne.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYGgoQCG1VCe42HzbzhAXR
This commit is contained in:
Claude committed 2026-08-14 06:12:25 +00:00
1 parent c9e1934aae
commit 8158eda11b
3 files changed
+31 -3

No files matched your search

+20
View File
@@ -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.
+2 -1
View File
@@ -394,7 +394,8 @@ async function buildProductRows(input: GetProductRowsInput): Promise<ProductRow[
const depuisStock: Record<string, number> = {};
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;
}
+9 -2
View File
@@ -450,10 +450,12 @@ function buildNomMap(rows: PgNomRow[]): Map<string, PgNomRow> {
* 1 requête SQL — remplace la partie stock du référentiel.
*/
export async function pgGetStockByFournisseur(codefou: string): Promise<Map<string, PgStockRow[]>> {
// `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<Map<stri
* tarifé n'est pas un article à 0 €.
*/
export async function pgGetPrixVenteByFournisseur(codefou: string): Promise<Map<string, Record<string, number>>> {
// 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