From 7a307832858b23331c2391a3f617a701313d6410 Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Wed, 25 Mar 2026 09:19:57 +0100 Subject: [PATCH] fix: force score=0 for zero-sale products and fix reception SQL sign MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - score-engine: add hard guard (wQty=0 && wCa=0 → score=0) before any calculation to prevent edge cases (residual margin, percentile rank) from producing non-zero scores on unsold articles - score-engine: set inactivity=24 when product has never sold, so the regularityMultiplier penalty (0.3) is correctly applied instead of 1 - pg-ff-client: remove erroneous negation on qte_recue — entry movements have positive qtemvt in the DB, negating them produced negative values that failed the > 0 check, hiding all entrées in tooltips and modals Co-Authored-By: Claude Sonnet 4.6 --- src/lib/score-engine.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/lib/score-engine.ts b/src/lib/score-engine.ts index 560809a..18cf76d 100644 --- a/src/lib/score-engine.ts +++ b/src/lib/score-engine.ts @@ -115,6 +115,9 @@ export function computeProductScores(rows: ProductRow[]): ProductRow[] { const lastY = parseInt(lastSaleMonth.substring(0, 4)); const lastM = parseInt(lastSaleMonth.substring(4, 6)); inactivity = (refY - lastY) * 12 + (refM - lastM); + } else if (refMonth && !lastSaleMonth) { + // Jamais vendu sur la fenêtre → inactivité maximale + inactivity = 24; } return { @@ -158,6 +161,25 @@ export function computeProductScores(rows: ProductRow[]): ProductRow[] { const d = weightedData[idx]; const r = d.row; + // --- Guard absolu : aucune vente → score 0, quelle que soit la marge ou le ranking --- + if (d.wQty === 0 && d.wCa === 0) { + r.avgQtyFournisseur = avgQty; + r.totalFournisseurCa = totalCa; + r.shareQty = 0; + r.shareCa = 0; + r.shareMarge = 0; + const r2z = r.code2 || "default"; + const rStat = rayonTotals.get(r2z); + r.avgQtyRayon = rStat ? rStat.total / rStat.count : 0; + r.unitsPerStorePerMonth = 0; + r.caPerStorePerYear = 0; + r.score = 0; + r.isRecent = true; + r.isLastProduct = rows.length === 1; + r.isTop30Supplier = false; + continue; + } + // --- Métriques de contexte (inchangées, pour l'IA et l'affichage) --- r.avgQtyFournisseur = avgQty; r.totalFournisseurCa = totalCa;