diff --git a/src/app/(dashboard)/hit-parade/client.tsx b/src/app/(dashboard)/hit-parade/client.tsx
index 8e3dc65..ec4b18a 100644
--- a/src/app/(dashboard)/hit-parade/client.tsx
+++ b/src/app/(dashboard)/hit-parade/client.tsx
@@ -28,6 +28,43 @@ interface Props {
pivotted: HitParadePivotRow[];
}
+function ColHeader({
+ label,
+ sortable,
+ group,
+ sortKey,
+ sortDir,
+ onSort,
+}: {
+ label: string;
+ sortable?: SortKey;
+ group?: "292" | "579" | "total";
+ sortKey: SortKey;
+ sortDir: SortDir;
+ onSort: (key: SortKey) => void;
+}) {
+ const isActive = sortable && sortKey === sortable;
+ const groupBg = group === "292" ? "bg-blue-50" : group === "579" ? "bg-violet-50" : group === "total" ? "bg-emerald-50" : "";
+ const activeBg = group === "292" ? "bg-blue-100 text-blue-700" : group === "579" ? "bg-violet-100 text-violet-700" : group === "total" ? "bg-emerald-100 text-emerald-700" : "bg-blue-50 text-blue-700";
+ return (
+
onSort(sortable) : undefined}
+ className={[
+ "px-2 py-2 text-center text-xs font-semibold whitespace-nowrap select-none",
+ sortable ? "cursor-pointer" : "",
+ isActive ? activeBg : `${groupBg} text-gray-600`,
+ ].join(" ")}
+ >
+ {label}
+ {sortable && (
+
+ {isActive ? (sortDir === "desc" ? "↓" : "↑") : ↕}
+
+ )}
+ |
+ );
+}
+
export function HitParadeClient({ dateDebut, dateFin, pivotted }: Props) {
const router = useRouter();
const [sortKey, setSortKey] = useState(DEFAULT_SORT);
@@ -73,6 +110,32 @@ export function HitParadeClient({ dateDebut, dateFin, pivotted }: Props) {
const isSorted = sortKey !== DEFAULT_SORT || sortDir !== DEFAULT_DIR;
+ const filtered = useMemo(() =>
+ pivotted.filter(r =>
+ (filterFournisseur === "Tous" || r.fournisseur === filterFournisseur) &&
+ (filterNomenclature === "Tous" || r.nomenclature_code === filterNomenclature)
+ ),
+ [pivotted, filterFournisseur, filterNomenclature]
+ );
+
+ const sorted = useMemo(() =>
+ [...filtered].sort((a, b) => {
+ const diff = a[sortKey] - b[sortKey];
+ return sortDir === "desc" ? -diff : diff;
+ }),
+ [filtered, sortKey, sortDir]
+ );
+
+ const totals = useMemo(() => sorted.reduce(
+ (acc, r) => ({
+ 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, stock292: 0, stock579: 0, stockTotal: 0 }
+ ), [sorted]);
+
function exportToExcel() {
const headers = [
"Code", "Désignation", "Fournisseur", "Nomenclature",
@@ -115,56 +178,6 @@ export function HitParadeClient({ dateDebut, dateFin, pivotted }: Props) {
XLSX.writeFile(wb, `hit-parade_${dateDebut}_${dateFin}.xlsx`);
}
- const filtered = useMemo(() =>
- pivotted.filter(r =>
- (filterFournisseur === "Tous" || r.fournisseur === filterFournisseur) &&
- (filterNomenclature === "Tous" || r.nomenclature_code === filterNomenclature)
- ),
- [pivotted, filterFournisseur, filterNomenclature]
- );
-
- const sorted = useMemo(() =>
- [...filtered].sort((a, b) => {
- const diff = a[sortKey] - b[sortKey];
- return sortDir === "desc" ? -diff : diff;
- }),
- [filtered, sortKey, sortDir]
- );
-
- const totals = useMemo(() => sorted.reduce(
- (acc, r) => ({
- 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, stock292: 0, stock579: 0, stockTotal: 0 }
- ), [sorted]);
-
- function ColHeader({ label, sortable, k, group }: { label: string; sortable?: SortKey; k: string; group?: "292" | "579" | "total" }) {
- const isActive = sortable && sortKey === sortable;
- const groupBg = group === "292" ? "bg-blue-50" : group === "579" ? "bg-violet-50" : group === "total" ? "bg-emerald-50" : "";
- const activeBg = group === "292" ? "bg-blue-100 text-blue-700" : group === "579" ? "bg-violet-100 text-violet-700" : group === "total" ? "bg-emerald-100 text-emerald-700" : "bg-blue-50 text-blue-700";
- return (
- handleSort(sortable) : undefined}
- className={[
- "px-2 py-2 text-center text-xs font-semibold whitespace-nowrap select-none",
- sortable ? "cursor-pointer" : "",
- isActive ? activeBg : `${groupBg} text-gray-600`,
- ].join(" ")}
- >
- {label}
- {sortable && (
-
- {isActive ? (sortDir === "desc" ? "↓" : "↑") : ↕}
-
- )}
- |
- );
- }
-
return (
{/* Barre de contrôles */}
@@ -277,22 +290,22 @@ export function HitParadeClient({ dateDebut, dateFin, pivotted }: Props) {
-
-
+
+
| % Marge |
-
-
-
+
+
+
% Marge |
-
-
-
+
+
+
% Marge |
-
+
- {sorted.map((row, idx) => (
+ {sorted.map((row) => (
| {row.codein} |
{row.libelle.trim()} |
diff --git a/src/features/grid/api/get-product-rows.ts b/src/features/grid/api/get-product-rows.ts
index 102bb76..725257f 100644
--- a/src/features/grid/api/get-product-rows.ts
+++ b/src/features/grid/api/get-product-rows.ts
@@ -391,8 +391,10 @@ async function reconcileSelectedStoreFromMensuelApi(
for (const entry of entries) {
const period = entry.mois.replace("-", "");
if (!sortedPeriods.includes(period)) continue;
- const qty = Math.abs(Number(entry.ventes?.qte_vendue ?? 0) || 0);
- const ca = Math.abs(Number(entry.ventes?.ca_ht ?? 0) || 0);
+ // qte_vendue / ca_ht sont NÉGATIFS côté API (ventes nettes) : on
+ // les nie au lieu de Math.abs pour que les retours restent déduits.
+ const qty = -(Number(entry.ventes?.qte_vendue ?? 0) || 0);
+ const ca = -(Number(entry.ventes?.ca_ht ?? 0) || 0);
const marge = Number(entry.ventes?.marge ?? 0) || 0;
const stock = Number(entry.stock_fin_mois ?? 0) || 0;
const receptions = Number(entry.receptions?.qte_recue ?? 0) || 0;