diff --git a/src/app/(dashboard)/hit-parade/client.tsx b/src/app/(dashboard)/hit-parade/client.tsx index b533e9d..bbd70cc 100644 --- a/src/app/(dashboard)/hit-parade/client.tsx +++ b/src/app/(dashboard)/hit-parade/client.tsx @@ -4,7 +4,7 @@ import { useRouter } from "next/navigation"; import { useState, useMemo } from "react"; import type { HitParadePivotRow } from "./page"; -type SortKey = "caTotal" | "ca292" | "ca579" | "qteTotal" | "qte292" | "qte579"; +type SortKey = "caTotal" | "ca292" | "ca579" | "qteTotal" | "qte292" | "qte579" | "stockTotal" | "stock292" | "stock579"; type SortDir = "desc" | "asc"; const DEFAULT_SORT: SortKey = "caTotal"; @@ -82,8 +82,9 @@ export function HitParadeClient({ dateDebut, dateFin, pivotted }: Props) { qte292: acc.qte292 + r.qte292, ca292: acc.ca292 + r.ca292, marge292: acc.marge292 + r.marge292, qte579: acc.qte579 + r.qte579, ca579: acc.ca579 + r.ca579, marge579: acc.marge579 + r.marge579, qteTotal: acc.qteTotal + r.qteTotal, caTotal: acc.caTotal + r.caTotal, margeTotal: acc.margeTotal + r.margeTotal, + stock292: acc.stock292 + r.stock292, stock579: acc.stock579 + r.stock579, stockTotal: acc.stockTotal + r.stockTotal, }), - { qte292: 0, ca292: 0, marge292: 0, qte579: 0, ca579: 0, marge579: 0, qteTotal: 0, caTotal: 0, margeTotal: 0 } + { qte292: 0, ca292: 0, marge292: 0, qte579: 0, ca579: 0, marge579: 0, qteTotal: 0, caTotal: 0, margeTotal: 0, stock292: 0, stock579: 0, stockTotal: 0 } ), [sorted]); function ColHeader({ label, sortable, k }: { label: string; sortable?: SortKey; k: string }) { @@ -189,6 +190,9 @@ export function HitParadeClient({ dateDebut, dateFin, pivotted }: Props) { Total Réseau + + Stock actuel + @@ -200,6 +204,9 @@ export function HitParadeClient({ dateDebut, dateFin, pivotted }: Props) { % Marge + + + @@ -235,6 +242,15 @@ export function HitParadeClient({ dateDebut, dateFin, pivotted }: Props) { {row.caTotal > 0 ? formatPct(row.caTotal, row.margeTotal) : ""} + + {row.stock292 > 0 ? formatQte(row.stock292) : —} + + + {row.stock579 > 0 ? formatQte(row.stock579) : —} + + + {row.stockTotal > 0 ? formatQte(row.stockTotal) : 0} + ))} @@ -252,6 +268,9 @@ export function HitParadeClient({ dateDebut, dateFin, pivotted }: Props) { {formatQte(totals.qteTotal)} {formatCA(totals.caTotal)} {formatPct(totals.caTotal, totals.margeTotal)} + {formatQte(totals.stock292)} + {formatQte(totals.stock579)} + {formatQte(totals.stockTotal)} diff --git a/src/app/(dashboard)/hit-parade/page.tsx b/src/app/(dashboard)/hit-parade/page.tsx index 0faf7e5..cca29c5 100644 --- a/src/app/(dashboard)/hit-parade/page.tsx +++ b/src/app/(dashboard)/hit-parade/page.tsx @@ -1,4 +1,4 @@ -import { pgGetHitParade } from "@/lib/pg-ff-client"; +import { pgGetHitParade, pgGetStockForCodeins } from "@/lib/pg-ff-client"; import { HitParadeClient } from "./client"; export interface HitParadePivotRow { @@ -14,6 +14,9 @@ export interface HitParadePivotRow { qteTotal: number; caTotal: number; margeTotal: number; + stock292: number; + stock579: number; + stockTotal: number; } function pivotHitParade( @@ -30,6 +33,7 @@ function pivotHitParade( qte292: 0, ca292: 0, marge292: 0, qte579: 0, ca579: 0, marge579: 0, qteTotal: 0, caTotal: 0, margeTotal: 0, + stock292: 0, stock579: 0, stockTotal: 0, }); } const entry = map.get(row.codein)!; @@ -71,6 +75,13 @@ export default async function HitParadePage(props: { const rows = await pgGetHitParade(dateDebut, dateFin); const pivotted = pivotHitParade(rows); + const codeins = pivotted.map(r => r.codein); + const stockMap = await pgGetStockForCodeins(codeins); + for (const row of pivotted) { + const s = stockMap.get(row.codein); + if (s) { row.stock292 = s.stock292; row.stock579 = s.stock579; row.stockTotal = s.stockTotal; } + } + return (
diff --git a/src/lib/pg-ff-client.ts b/src/lib/pg-ff-client.ts index 9327422..bc87df2 100644 --- a/src/lib/pg-ff-client.ts +++ b/src/lib/pg-ff-client.ts @@ -735,3 +735,41 @@ export async function pgGetHitParade(dateDebut: string, dateFin: string): Promis console.log(`[pg-ff] HitParade: ${result.rows.length} lignes pour ${dateDebut}→${dateFin}`); return result.rows as unknown as HitParadeRow[]; } + +// --------------------------------------------------------------------------- +// Stock par liste de codeins (pour Hit Parade) +// --------------------------------------------------------------------------- + +export interface StockBySite { + stock292: number; + stock579: number; + stockTotal: number; +} + +/** + * Retourne le stock actuel (stockdispo) par codein pour une liste de codeins. + * 1 seule requête bulk — utilisé pour enrichir le Hit Parade. + */ +export async function pgGetStockForCodeins(codeins: string[]): Promise> { + if (codeins.length === 0) return new Map(); + + const result = await pgNoParallel(sql` + SELECT + a.codein, + cs.site, + cs.stockdispo::float AS stockdispo + FROM cube_stock cs + JOIN articles a ON a.no_id = cs.artnoid + WHERE a.codein = ANY(${codeins}) + `); + + const map = new Map(); + for (const r of result.rows as Array<{ codein: string; site: string; stockdispo: number }>) { + if (!map.has(r.codein)) map.set(r.codein, { stock292: 0, stock579: 0, stockTotal: 0 }); + const entry = map.get(r.codein)!; + if (r.site === "292") entry.stock292 += r.stockdispo; + else if (r.site === "579") entry.stock579 += r.stockdispo; + entry.stockTotal += r.stockdispo; + } + return map; +}