Prix de vente : tenir compte d'un prix non ventilé par magasin

`cube_pv` a une clé `(artnoid, site)`, mais la base n'en porte qu'une ligne
par article — 428 530 lignes pour 427 857 articles, là où deux magasins
tarifés en donneraient le double. Le prix n'est donc pas ventilé : la
colonne serait restée vide dès qu'on consulte le magasin qui n'a pas la
ligne, alors même que l'article a un prix.

La cellule couvre maintenant les quatre cas : le prix du magasin consulté
quand il existe ; le prix unique quand il n'y a qu'un tarif, avec la mention
« prix unique, non ventilé par magasin » ; rien quand plusieurs prix
coexistent sans concerner ce magasin — celui du voisin n'est pas le sien ;
et, en « tous magasins », le prix commun ou le plus élevé assorti du « ≠ ».

Vérifié sur build de production, quatre articles couvrant les quatre cas,
dans les trois modes de magasin. Le cas décisif : prix porté par le seul
site 292, consulté depuis Houdemont — 12,90 € et la mention, au lieu du
« - » d'avant.

La route de diagnostic est retirée : `cube_pv` est confirmé alimenté et
synchronisé, elle a fait son office.

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 8158eda11b
commit 06f0bd0513
2 files changed
+29 -183

No files matched your search

+29 -15
View File
@@ -96,29 +96,43 @@ const fmtEuro2 = (v: number) =>
/**
* Prix de vente à montrer pour le magasin consulté.
*
* Sur un magasin donné, c'est SON prix. En « tous magasins », c'est le prix
* commun — et s'ils divergent, le plus élevé assorti d'un signal : une cellule
* ne peut afficher qu'un nombre, mais taire l'écart ferait passer le prix d'un
* magasin pour celui des deux.
* `cube_pv` a une clé `(artnoid, site)`, mais notre base n'y met en pratique
* qu'une ligne par article : le prix n'est pas ventilé par magasin. La colonne
* doit donc rester lisible dans les deux cas de figure —
*
* · magasin consulté tarifé → SON prix ;
* · prix unique, non ventilé → ce prix vaut pour tous les magasins ;
* · plusieurs prix mais aucun pour ce magasin → rien, plutôt que celui du
* voisin ;
* · en « tous magasins » : le prix commun, et s'ils divergent le plus élevé
* assorti d'un signal. Une cellule ne peut afficher qu'un nombre, mais
* taire l'écart ferait passer le prix d'un magasin pour celui des deux.
*/
function prixVenteAffiche(row: ProductRow, activeMagasin: string): { valeur: number | null; divergent: boolean; detail: string | null } {
const parSite = row.prixVenteByStore;
const entrees = Object.entries(row.prixVenteByStore ?? {});
if (activeMagasin !== "TOTAL") {
const pv = parSite?.[activeMagasin];
return { valeur: pv ?? null, divergent: false, detail: null };
}
const entrees = Object.entries(parSite ?? {});
// Aucun prix ventilé : reste le PV de la fiche article, s'il existe.
if (entrees.length === 0) return { valeur: row.prixVente ?? null, divergent: false, detail: null };
// Comparaison au centime : deux flottants issus du même prix peuvent différer
// dans les décimales lointaines sans que le prix affiché change.
const distincts = new Set(entrees.map(([, pv]) => Math.round(pv * 100)));
const detail = entrees
.sort(([a], [b]) => a.localeCompare(b))
.map(([site, pv]) => `${SITE_LABELS[site]?.nom ?? site} : ${fmtEuro2(pv)}`)
.join(" · ");
if (activeMagasin !== "TOTAL") {
const pv = row.prixVenteByStore?.[activeMagasin];
if (pv != null) return { valeur: pv, divergent: false, detail: null };
if (distincts.size === 1) {
return { valeur: entrees[0][1], divergent: false, detail: "prix unique, non ventilé par magasin" };
}
return { valeur: null, divergent: false, detail: null };
}
const detail = entrees.length > 1
? entrees
.sort(([a], [b]) => a.localeCompare(b))
.map(([site, pv]) => `${SITE_LABELS[site]?.nom ?? site} : ${fmtEuro2(pv)}`)
.join(" · ")
: null;
return {
valeur: Math.max(...entrees.map(([, pv]) => pv)),