mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-13 02:07:37 +02:00
feat(grid): ajout algorithme score produit avec parametrage dynamique et page d'aide
This commit is contained in:
1 parent
f540e4f2b3
commit
3544c0c898
67 files changed
+19476
-15
No files matched your search
@@ -0,0 +1,69 @@
|
||||
"use server";
|
||||
|
||||
import { db } from "@/db";
|
||||
import { ventesProduits } from "@/db/schema";
|
||||
import { eq, and, ne, sql } from "drizzle-orm";
|
||||
import type { ProductRow, GammeCode } from "@/types/grid";
|
||||
import { computeProductScores } from "@/lib/score-engine";
|
||||
|
||||
interface GetProductRowsInput {
|
||||
codeFournisseur: string;
|
||||
magasin?: string;
|
||||
}
|
||||
|
||||
export async function getProductRows(input: GetProductRowsInput): Promise<ProductRow[]> {
|
||||
const { codeFournisseur, magasin = "TOTAL" } = input;
|
||||
|
||||
// Get all rows for this supplier (excluding the synthetic TOTAL period)
|
||||
const rows = await db
|
||||
.select()
|
||||
.from(ventesProduits)
|
||||
.where(
|
||||
and(
|
||||
eq(ventesProduits.codeFournisseur, codeFournisseur),
|
||||
ne(ventesProduits.periode, "TOTAL"),
|
||||
magasin !== "TOTAL" ? eq(ventesProduits.magasin, magasin) : ne(ventesProduits.magasin, "TOTAL"),
|
||||
)
|
||||
);
|
||||
|
||||
// Group by codein and aggregate monthly sales
|
||||
const productMap = new Map<string, ProductRow>();
|
||||
|
||||
for (const row of rows) {
|
||||
const existing = productMap.get(row.codein);
|
||||
const monthKey = row.periode;
|
||||
const qty = Math.abs(parseFloat(row.quantite?.toString() ?? "0"));
|
||||
const ca = Math.abs(parseFloat(row.montantMvt?.toString() ?? "0"));
|
||||
const marge = Math.abs(parseFloat(row.margeMvt?.toString() ?? "0"));
|
||||
|
||||
if (existing) {
|
||||
existing.sales12m[monthKey] = (existing.sales12m[monthKey] ?? 0) + qty;
|
||||
existing.totalQuantite += qty;
|
||||
existing.totalCa += ca;
|
||||
existing.totalMarge += marge;
|
||||
existing.tauxMarge = existing.totalCa > 0 ? (existing.totalMarge / existing.totalCa) * 100 : 0;
|
||||
} else {
|
||||
productMap.set(row.codein, {
|
||||
codein: row.codein,
|
||||
codeFournisseur: row.codeFournisseur ?? "",
|
||||
nomFournisseur: row.nomFournisseur ?? "",
|
||||
libelle1: row.libelle1 ?? "",
|
||||
gtin: row.gtin ?? "",
|
||||
reference: row.reference ?? "",
|
||||
code3: row.code3 ?? "",
|
||||
libelle3: row.libelle3 ?? "",
|
||||
codeGamme: (row.codeGamme as GammeCode | null),
|
||||
codeGammeDraft: null,
|
||||
sales12m: { [monthKey]: qty },
|
||||
totalQuantite: qty,
|
||||
totalCa: ca,
|
||||
totalMarge: marge,
|
||||
tauxMarge: ca > 0 ? (marge / ca) * 100 : 0,
|
||||
score: 0,
|
||||
aiRecommendation: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return computeProductScores(Array.from(productMap.values()));
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
"use server";
|
||||
|
||||
import { db } from "@/db";
|
||||
import { ventesProduits } from "@/db/schema";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
|
||||
const SaveDraftsSchema = z.object({
|
||||
changes: z.array(
|
||||
z.object({
|
||||
codein: z.string(),
|
||||
magasin: z.string(),
|
||||
codeGamme: z.enum(["A", "B", "C", "Z"]),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
export async function saveDraftChanges(
|
||||
raw: unknown
|
||||
): Promise<{ success: boolean; saved: number; error?: string }> {
|
||||
const parsed = SaveDraftsSchema.safeParse(raw);
|
||||
if (!parsed.success) {
|
||||
return { success: false, saved: 0, error: "Validation failed: " + parsed.error.message };
|
||||
}
|
||||
|
||||
const { changes } = parsed.data;
|
||||
|
||||
try {
|
||||
// Batch update all rows for given codein × magasin
|
||||
await Promise.all(
|
||||
changes.map(({ codein, magasin, codeGamme }) =>
|
||||
db
|
||||
.update(ventesProduits)
|
||||
.set({ codeGamme })
|
||||
.where(
|
||||
and(
|
||||
eq(ventesProduits.codein, codein),
|
||||
eq(ventesProduits.magasin, magasin)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return { success: true, saved: changes.length };
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : "Unknown error";
|
||||
return { success: false, saved: 0, error: msg };
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user