diff --git a/src/features/ai-copilot/business/scoring-engine.ts b/src/features/ai-copilot/business/scoring-engine.ts index ae388b2..62c3d32 100644 --- a/src/features/ai-copilot/business/scoring-engine.ts +++ b/src/features/ai-copilot/business/scoring-engine.ts @@ -90,7 +90,7 @@ export class ScoringEngine { let recommendation: "A" | "Z" = compositeScore >= threshold ? "A" : "Z"; // 7. Gardes-fous et Labels - const isRecent = (target.regularityScore || 0) < 3; + const isRecent = (target.regularityScore || 0) < 3 && (target.inactivityMonths || 0) <= 1; const isTop30Supplier = this.checkTop30Supplier(target, processedProducts, compositeScore); const isLastProduct = target.isLastProductOfSupplier || false; diff --git a/src/features/grid/components/bulk-ai-analyzer.tsx b/src/features/grid/components/bulk-ai-analyzer.tsx index 296d8ad..401f7d7 100644 --- a/src/features/grid/components/bulk-ai-analyzer.tsx +++ b/src/features/grid/components/bulk-ai-analyzer.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useState, useRef } from "react"; +import React, { useState, useRef, useEffect } from "react"; import { useGridStore } from "@/features/grid/store/use-grid-store"; import { useAiCopilotStore } from "@/features/ai-copilot/store/use-ai-copilot-store"; import { ScoringEngine } from "@/features/ai-copilot/business/scoring-engine"; @@ -23,6 +23,16 @@ export function BulkAiAnalyzer() { const setDraftGamme = useGridStore((s) => s.setDraftGamme); const { setInsight, batchSetLoading, setError } = useAiCopilotStore(); + const prevSupplierRef = useRef(supplierCode); + useEffect(() => { + if (supplierCode !== prevSupplierRef.current) { + if (!isAnalyzing) { + setProgress({ current: 0, total: 0, message: "", errors: 0 }); + } + prevSupplierRef.current = supplierCode; + } + }, [supplierCode, isAnalyzing]); + const handleStop = () => { isCancelledRef.current = true; setProgress((prev) => ({ ...prev, message: "Arrêt en cours..." })); diff --git a/verify-fix.ts b/verify-fix.ts new file mode 100644 index 0000000..df94216 --- /dev/null +++ b/verify-fix.ts @@ -0,0 +1,27 @@ + +import { ScoringEngine } from "../src/features/ai-copilot/business/scoring-engine"; +import { ProductAnalysisInput } from "../src/features/ai-copilot/models/ai-analysis.types"; + +const mockProducts: ProductAnalysisInput[] = [ + { codein: "PROD1", libelle1: "Nouveau Récent", regularityScore: 1, inactivityMonths: 0, totalCa: 100, totalQuantite: 10, tauxMarge: 30, score: 50, storeCount: 1, sales12m: {} }, + { codein: "PROD2", libelle1: "Nouveau Ancien", regularityScore: 1, inactivityMonths: 6, totalCa: 100, totalQuantite: 10, tauxMarge: 30, score: 50, storeCount: 1, sales12m: {} }, + { codein: "PROD3", libelle1: "Ancien Actif", regularityScore: 12, inactivityMonths: 0, totalCa: 1000, totalQuantite: 100, tauxMarge: 30, score: 50, storeCount: 1, sales12m: {} }, + { codein: "PROD4", libelle1: "Nouveau Limite", regularityScore: 2, inactivityMonths: 1, totalCa: 100, totalQuantite: 10, tauxMarge: 30, score: 50, storeCount: 1, sales12m: {} }, + { codein: "PROD5", libelle1: "Nouveau Hors Limite", regularityScore: 2, inactivityMonths: 2, totalCa: 100, totalQuantite: 10, tauxMarge: 30, score: 50, storeCount: 1, sales12m: {} }, +]; + +console.log("--- Testing isRecent Logic ---"); + +mockProducts.forEach(p => { + const result = ScoringEngine.analyzeRayon(p, mockProducts); + console.log(`Product: ${p.libelle1} (reg: ${p.regularityScore}, inact: ${p.inactivityMonths})`); + console.log(` isRecent: ${result.decision.isRecent}`); + console.log(` Recommendation: ${result.decision.recommendation}`); +}); + +// Expectations: +// PROD1: true (1 < 3 && 0 <= 1) +// PROD2: false (1 < 3 && 6 > 1) -> Recommendation should NOT be protected (unless score high) +// PROD3: false (12 >= 3) +// PROD4: true (2 < 3 && 1 <= 1) +// PROD5: false (2 < 3 && 2 > 1) diff --git a/verify-logic.js b/verify-logic.js new file mode 100644 index 0000000..72ae854 --- /dev/null +++ b/verify-logic.js @@ -0,0 +1,32 @@ + +// Mocking the simplified logic for validation without imports hitting environment issues +function isRecentLogic(regularityScore, inactivityMonths) { + return (regularityScore || 0) < 3 && (inactivityMonths || 0) <= 1; +} + +const testCases = [ + { name: "Nouveau Récent", regularity: 1, inactivity: 0, expected: true }, + { name: "Nouveau Limite", regularity: 2, inactivity: 1, expected: true }, + { name: "Nouveau Ancien (6m)", regularity: 1, inactivity: 6, expected: false }, + { name: "Nouveau Hors Limite (2m)", regularity: 2, inactivity: 2, expected: false }, + { name: "Ancien Actif", regularity: 12, inactivity: 0, expected: false }, + { name: "VRAIMENT Nouveau (0 activité)", regularity: 0, inactivity: 0, expected: true } +]; + +console.log("--- Testing isRecent Logic (Simplified) ---"); +let allPassed = true; + +testCases.forEach(tc => { + const actual = isRecentLogic(tc.regularity, tc.inactivity); + const passed = actual === tc.expected; + console.log(`${passed ? "✅" : "❌"} [${tc.name}]: expected ${tc.expected}, got ${actual}`); + if (!passed) allPassed = false; +}); + +if (allPassed) { + console.log("\nALL LOGIC TESTS PASSED"); + process.exit(0); +} else { + console.log("\nSOME TESTS FAILED"); + process.exit(1); +}