mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-11 17:26:32 +02:00
feat: implement AI copilot scoring engine for product analysis and recommendations, including a verification script for recent product logic.
This commit is contained in:
1 parent
56b09a438d
commit
a008b40a59
4 files changed
+71
-2
No files matched your search
@@ -90,7 +90,7 @@ export class ScoringEngine {
|
|||||||
let recommendation: "A" | "Z" = compositeScore >= threshold ? "A" : "Z";
|
let recommendation: "A" | "Z" = compositeScore >= threshold ? "A" : "Z";
|
||||||
|
|
||||||
// 7. Gardes-fous et Labels
|
// 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 isTop30Supplier = this.checkTop30Supplier(target, processedProducts, compositeScore);
|
||||||
const isLastProduct = target.isLastProductOfSupplier || false;
|
const isLastProduct = target.isLastProductOfSupplier || false;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"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 { useGridStore } from "@/features/grid/store/use-grid-store";
|
||||||
import { useAiCopilotStore } from "@/features/ai-copilot/store/use-ai-copilot-store";
|
import { useAiCopilotStore } from "@/features/ai-copilot/store/use-ai-copilot-store";
|
||||||
import { ScoringEngine } from "@/features/ai-copilot/business/scoring-engine";
|
import { ScoringEngine } from "@/features/ai-copilot/business/scoring-engine";
|
||||||
@@ -23,6 +23,16 @@ export function BulkAiAnalyzer() {
|
|||||||
const setDraftGamme = useGridStore((s) => s.setDraftGamme);
|
const setDraftGamme = useGridStore((s) => s.setDraftGamme);
|
||||||
const { setInsight, batchSetLoading, setError } = useAiCopilotStore();
|
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 = () => {
|
const handleStop = () => {
|
||||||
isCancelledRef.current = true;
|
isCancelledRef.current = true;
|
||||||
setProgress((prev) => ({ ...prev, message: "Arrêt en cours..." }));
|
setProgress((prev) => ({ ...prev, message: "Arrêt en cours..." }));
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user