feat(grid): implement hierarchical nomenclature filter (Secteur > Rayon > Famille)

This commit is contained in:
Michael committed 2026-02-21 17:27:33 +01:00
1 parent de7aa1560e
commit 33615bb1bd
10 files changed
+310 -57

No files matched your search

+28 -13
View File
@@ -2,37 +2,48 @@
import { db } from "@/db";
import { ventesProduits } from "@/db/schema";
import { eq, and, ne } from "drizzle-orm";
import type { ProductRow, GammeCode } from "@/types/grid";
import { eq, and, ne, like } from "drizzle-orm";
import type { ProductRow, GammeCode, GridFilters } from "@/types/grid";
import { computeProductScores } from "@/lib/score-engine";
interface GetProductRowsInput {
codeFournisseur: string;
magasin?: string;
filters?: Partial<GridFilters>;
}
export async function getProductRows(input: GetProductRowsInput): Promise<ProductRow[]> {
const { codeFournisseur, magasin = "TOTAL" } = input;
const { codeFournisseur, magasin = "TOTAL", filters } = input;
// Calculer les 12 derniers mois COMPLETS (excluant le mois en cours)
const allowedPeriods = new Set<string>();
// ... (allowedPeriods calculation remains the same)
const now = new Date();
const allowedPeriods = new Set<string>();
for (let i = 12; i >= 1; i--) {
const d = new Date(now.getFullYear(), now.getMonth() - i, 1);
allowedPeriods.add(`${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}`);
}
// Get all rows for this supplier (excluding the synthetic TOTAL period)
// Build conditions
const conditions = [
eq(ventesProduits.codeFournisseur, codeFournisseur),
ne(ventesProduits.periode, "TOTAL"),
magasin !== "TOTAL" ? eq(ventesProduits.magasin, magasin) : ne(ventesProduits.magasin, "TOTAL"),
];
// Hierarchical filtering
if (filters?.code3) {
conditions.push(eq(ventesProduits.code3, filters.code3));
} else if (filters?.code2) {
conditions.push(like(ventesProduits.code3, `${filters.code2}%`));
} else if (filters?.code1) {
conditions.push(like(ventesProduits.code3, `${filters.code1}%`));
}
// Get all rows for this supplier
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"),
)
);
.where(and(...conditions));
// Group by codein and aggregate monthly sales + track store participation
const productMap = new Map<string, ProductRow>();
@@ -69,6 +80,10 @@ export async function getProductRows(input: GetProductRowsInput): Promise<Produc
libelle1: row.libelle1 ?? "",
gtin: row.gtin ?? "",
reference: row.reference ?? "",
code1: (row.code3 ?? "").slice(0, 2),
libelleNiveau1: "Niv. 1", // À mapper plus tard
code2: (row.code3 ?? "").slice(0, 4),
libelleNiveau2: "Niv. 2", // À mapper plus tard
code3: row.code3 ?? "",
libelle3: row.libelle3 ?? "",
codeGamme: (row.codeGamme as GammeCode | null),