diff --git a/src/features/grid/api/get-product-rows.ts b/src/features/grid/api/get-product-rows.ts index 66c5287..477f741 100644 --- a/src/features/grid/api/get-product-rows.ts +++ b/src/features/grid/api/get-product-rows.ts @@ -288,6 +288,25 @@ async function buildProductRows(input: GetProductRowsInput): Promise 0) { + for (const periode of sortedPeriods) { + product.stock12m[periode] = sites.reduce( + (total, site) => total + (product.stock12mByStore![site][periode] ?? 0), + 0, + ); + } + } } // workingStores : sites avec au moins 1 vente sur les 12 derniers mois @@ -396,7 +415,14 @@ async function buildProductRows(input: GetProductRowsInput): Promise retenus.length) { + console.warn( + `[getProductRows] rattrapage magasin ${magasin} borné à ${retenus.length} articles` + + ` sur ${candidates.length} : les autres gardent la valeur SQL.`, + ); + } + try { const mensuelMap = await getMensuelByArticles( - candidates.map((row) => ({ + retenus.map((row) => ({ codein: row.codein, libelle1: row.libelle1, codefou: row.codeFournisseur, @@ -431,7 +470,7 @@ async function reconcileSelectedStoreFromMensuelApi( ); let fixedRows = 0; - for (const row of candidates) { + for (const row of retenus) { const entries = (mensuelMap.get(row.codein) ?? []).filter((entry) => entry.site === magasin); if (entries.length === 0) continue; diff --git a/src/features/grid/components/heatmap-grid.tsx b/src/features/grid/components/heatmap-grid.tsx index 0a75f15..85ccc0e 100644 --- a/src/features/grid/components/heatmap-grid.tsx +++ b/src/features/grid/components/heatmap-grid.tsx @@ -413,10 +413,14 @@ export function HeatmapGrid({ onSelectionChange, isAdmin }: HeatmapGridProps) { // Filtre client-side par code3 (famille) et codeGamme const filteredData = useMemo(() => { const { code3, codeGamme } = filters; - if (!code3 && !codeGamme) return rows; + // Garde : un état persisté d'une version antérieure peut encore contenir + // une chaîne. La migration du store le corrige, mais un `new Set("320211")` + // produirait un ensemble de caractères et viderait la Grille sans rien dire. + const codes = Array.isArray(code3) ? code3 : typeof code3 === "string" ? [code3] : null; + if (!codes && !codeGamme) return rows; // Set plutôt que includes() : la liste peut compter des dizaines de // nomenclatures, et le filtre est réévalué pour chaque ligne. - const nomenclatures = code3 ? new Set(code3) : null; + const nomenclatures = codes ? new Set(codes) : null; return rows.filter(r => { if (nomenclatures && !nomenclatures.has(r.code3)) return false; if (codeGamme) { diff --git a/src/features/grid/store/use-grid-store.ts b/src/features/grid/store/use-grid-store.ts index 5449acc..82977d4 100644 --- a/src/features/grid/store/use-grid-store.ts +++ b/src/features/grid/store/use-grid-store.ts @@ -72,6 +72,12 @@ function computeSummary(rows: ProductRow[], drafts: Record, m }; } +/** Sous-ensemble réellement écrit dans le navigateur (cf. `partialize`). */ +type GridPersisted = Pick< + GridState, + "filters" | "displayDensity" | "draftChanges" | "activeGridQuery" | "columnVisibility" | "columnSizing" +>; + export const useGridStore = create()( persist( (set, get) => ({ @@ -219,8 +225,24 @@ export const useGridStore = create()( }), { name: "collectflow-grid-storage", + /** + * v1 — `filters.code3` est passé d'une chaîne unique à `string[] | null` + * (filtre multi-nomenclatures). Sans migration, la valeur déjà présente + * dans le navigateur restait une chaîne : `new Set("320211")` produit un + * ensemble de caractères, plus aucune ligne ne correspond, et la Grille + * apparaît vide. Le numéro de version force la reprise de l'existant. + */ + version: 1, + migrate: (persisted: unknown, version: number): GridPersisted => { + const etat = (persisted ?? {}) as GridPersisted & { filters?: Record }; + if (version < 1 && etat.filters) { + const c = etat.filters.code3; + etat.filters.code3 = typeof c === "string" && c !== "" ? [c] : Array.isArray(c) ? c : null; + } + return etat; + }, // Only persist filters, display density, drafts, active grid query, and column visibility/sizing - partialize: (state) => ({ + partialize: (state): GridPersisted => ({ filters: state.filters, displayDensity: state.displayDensity, draftChanges: state.draftChanges,