Afficher les montants au centime dans toute l'application

Les CA, marges et valeurs de stock étaient arrondis à l'euro (fmtEur0,
fmtEntier suivi de « € », Math.round dans les exports). Tous les montants
sont désormais affichés avec deux décimales : accueil, grille, fiche
produit, hit-parade, publicités, analytics, snapshots, export PDF et
export Excel des manques d'assortiment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VEXT61tGbsXHUDNBeghCXQ
This commit is contained in:
Claude committed 2026-10-05 07:50:59 +00:00
1 parent ad5da8f95d
commit 4a11bdc3b0
13 files changed
+58 -55

No files matched your search

@@ -30,7 +30,6 @@ const PROFONDEURS = [100, 200, 500] as const;
export const PROFONDEUR_MAX = PROFONDEURS[PROFONDEURS.length - 1];
const nb = (v: number) => Math.round(v).toLocaleString("fr-FR");
const eur0 = (v: number) => `${Math.round(v).toLocaleString("fr-FR")} €`;
const eur2 = (v: number) => v.toLocaleString("fr-FR", { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + " €";
/**
@@ -51,18 +50,18 @@ interface Critere {
}
const CRITERES: Record<string, Critere> = {
caReseau: { label: "CA réseau", format: eur0, xlsx: '#,##0 "€"' },
caReseau: { label: "CA réseau", format: eur2, xlsx: '#,##0.00 "€"' },
qteReseau: { label: "Qté réseau", format: nb, xlsx: "#,##0" },
nbMagasinsReseau: { label: "Magasins réseau", format: nb, xlsx: "#,##0" },
tendanceReseau: { label: "Tendance réseau", format: (v) => `${v >= 0 ? "+" : ""}${Math.round(v * 100)} %`, xlsx: "0 %" },
prixMoyenReseau: { label: "PV moyen réseau", format: eur2, xlsx: '#,##0.00 "€"' },
prixVente: { label: "PV magasin", format: eur2, xlsx: '#,##0.00 "€"' },
caParMagasinReseau: { label: "CA / magasin réseau", format: eur0, xlsx: '#,##0 "€"' },
caParMagasinReseau: { label: "CA / magasin réseau", format: eur2, xlsx: '#,##0.00 "€"' },
margePctReseau: { label: "Marge % réseau", format: (v) => `${(Math.abs(v) <= 1 ? v * 100 : v).toFixed(1)} %`,
xlsx: '0.0 "%"', brut: (v) => (Math.abs(v) <= 1 ? v * 100 : v) },
totalQuantite: { label: "Qté 12 m", format: nb, xlsx: "#,##0" },
totalCa: { label: "CA 12 m", format: eur0, xlsx: '#,##0 "€"' },
totalMarge: { label: "Marge 12 m", format: eur0, xlsx: '#,##0 "€"' },
totalCa: { label: "CA 12 m", format: eur2, xlsx: '#,##0.00 "€"' },
totalMarge: { label: "Marge 12 m", format: eur2, xlsx: '#,##0.00 "€"' },
};
function libelleCritere(id: string | null): string {
@@ -19,7 +19,7 @@ import {
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { confirmer, toast } from "@/components/ui/feedback";
import { fmtDecimal1, fmtEntier, fmtEur0 } from "@/lib/format";
import { fmtDecimal1, fmtEntier, fmtEur2 } from "@/lib/format";
import { couleurMarge } from "@/lib/marge";
import {
exporterFichierGammes,
@@ -165,10 +165,10 @@ function FloatingSummaryBarInner({ isAdmin, nomFournisseur }: { isAdmin: boolean
<div className="flex flex-wrap items-center gap-x-8 gap-y-1">
<Stat label="Produits actifs" value={fmtEntier(summary.totalRows)} sub={summary.totalProducts !== summary.totalRows ? `sur ${fmtEntier(summary.totalProducts)}` : undefined} subColor="var(--text-muted)" />
<Stat label="Quantités vendues (12 mois)" value={fmtEntier(summary.totalQuantite)} />
<Stat label="Chiffre d'affaires" value={fmtEur0(summary.totalCa)} />
<Stat label="Chiffre d'affaires" value={fmtEur2(summary.totalCa)} />
<Stat
label="Marge"
value={fmtEur0(summary.totalMarge)}
value={fmtEur2(summary.totalMarge)}
sub={`${fmtDecimal1(summary.tauxMargeGlobal)} %`}
subColor={couleurMarge(summary.tauxMargeGlobal)}
/>
@@ -45,7 +45,7 @@ import {
} from "@/features/grid/lib/network-trend";
import { TrendSparkline } from "@/features/grid/components/network-charts";
import type { NetworkTrend } from "@/features/grid/lib/network-trend";
import { fmtDecimal1, fmtEntier, fmtEur0, fmtEur2 } from "@/lib/format";
import { fmtDecimal1, fmtEntier, fmtEur2 } from "@/lib/format";
import { CLASSES_SANS_GAMME, trouverGamme } from "@/lib/gammes";
import { couleurMarge } from "@/lib/marge";
import { Button } from "@/components/ui/button";
@@ -891,7 +891,7 @@ function HeatmapGridInner({ codeFournisseur, onSelectionChange, isAdmin, nomFour
const val = row.original.caReseau;
return (
<div className="text-center tabular-nums text-[12px] font-bold text-emerald-600 dark:text-emerald-400">
{val != null ? fmtEur0(val) : "-"}
{val != null ? fmtEur2(val) : "-"}
</div>
);
},
@@ -1020,7 +1020,7 @@ function HeatmapGridInner({ codeFournisseur, onSelectionChange, isAdmin, nomFour
const val = row.original.caParMagasinReseau;
return (
<div className="text-center tabular-nums text-[12px] font-bold" style={{ color: "var(--text-secondary)" }}>
{val != null ? fmtEur0(val) : "-"}
{val != null ? fmtEur2(val) : "-"}
</div>
);
},
@@ -1110,14 +1110,14 @@ function HeatmapGridInner({ codeFournisseur, onSelectionChange, isAdmin, nomFour
id: "totalCa",
accessorFn: (row: ProductRow) => activeMagasin === "TOTAL" ? row.totalCa : (row.caByStore?.[activeMagasin] ?? 0),
header: () => <div className="text-center w-full" title="Chiffre d'affaires sur 12 mois">CA</div>,
size: 90,
size: 105,
cell: ({ row }: { row: { original: ProductRow } }) => {
const ca = activeMagasin === "TOTAL"
? row.original.totalCa
: (row.original.caByStore?.[activeMagasin] ?? 0);
return (
<div className="text-center tabular-nums text-[13px] font-bold" style={{ color: "var(--text-primary)" }}>
{fmtEntier(ca)}&nbsp;€
{fmtEur2(ca)}
</div>
);
},
@@ -1126,7 +1126,7 @@ function HeatmapGridInner({ codeFournisseur, onSelectionChange, isAdmin, nomFour
id: "totalMarge",
accessorFn: (row: ProductRow) => activeMagasin === "TOTAL" ? row.totalMarge : (row.margeByStore?.[activeMagasin] ?? 0),
header: () => <div className="text-center w-full" title="Marge en euros et taux de marge sur 12 mois">Marge</div>,
size: 110,
size: 115,
cell: ({ row }: { row: { original: ProductRow } }) => {
const marge = activeMagasin === "TOTAL"
? row.original.totalMarge
@@ -1138,7 +1138,7 @@ function HeatmapGridInner({ codeFournisseur, onSelectionChange, isAdmin, nomFour
return (
<div className="flex flex-col items-center justify-center">
<span className="tabular-nums text-[13px] font-bold" style={{ color: "var(--text-primary)" }}>
{fmtEntier(marge)}&nbsp;€
{fmtEur2(marge)}
</span>
<span className="tabular-nums text-xs font-semibold" style={{ color: couleurMarge(taux) }}>
{taux.toFixed(1)}%
@@ -33,7 +33,7 @@ import {
} from "@/features/grid/lib/network-trend";
import { formatDate, formatMonthLabel, qlikMonthToFf } from "@/features/grid/lib/months";
import { MAGASINS, LIBELLE_TOUS_MAGASINS } from "@/lib/magasins";
import { fmtEur0 } from "@/lib/format";
import { fmtEur2 } from "@/lib/format";
import type { ProductRow } from "@/types/grid";
const fmt = (v: number, d = 0) => v.toLocaleString("fr-FR", { minimumFractionDigits: d, maximumFractionDigits: d });
@@ -285,7 +285,7 @@ export function NetworkMonthlyModal({ row, trend, mois, onClose }: {
{row.caReseau != null && row.caReseau > 0 && (
<TuileStat
label="CA réseau 12 m"
valeur={fmtEur0(row.caReseau)}
valeur={fmtEur2(row.caReseau)}
indice={row.margePctReseau ? `marge ${fmt(row.margePctReseau * 100, 1)} %` : undefined}
/>
)}
+3 -2
View File
@@ -143,6 +143,7 @@ export async function imprimerPdf(nomFournisseur: string): Promise<boolean> {
doc.setTextColor(100);
doc.text(`Export généré le : ${new Date().toLocaleDateString("fr-FR")}`, 14, 30);
const eur = (v: number) => v.toLocaleString("fr-FR", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const head = [["Code-barres", "Code article", "Réf.", "Libellé", "Magasins réseau", "Qté", "CA", "Marge", "Gamme"]];
const body = rows.map((r) => [
r.gtin || r.codein,
@@ -151,8 +152,8 @@ export async function imprimerPdf(nomFournisseur: string): Promise<boolean> {
r.libelle1 ? r.libelle1.substring(0, 60) + (r.libelle1.length > 60 ? "..." : "") : "",
r.nbMagasinsReseau != null ? r.nbMagasinsReseau.toString() : "-",
Math.round(r.totalQuantite).toLocaleString("fr-FR"),
`${Math.round(r.totalCa).toLocaleString("fr-FR")} €`,
`${Math.round(r.totalMarge).toLocaleString("fr-FR")} €\n(${r.tauxMarge.toFixed(1)}%)`,
`${eur(r.totalCa)} €`,
`${eur(r.totalMarge)} €\n(${r.tauxMarge.toFixed(1)}%)`,
gammeEffective(r, draftChanges) || "-",
]);
@@ -19,7 +19,7 @@ import { Button } from "@/components/ui/button";
import { StoreBadge } from "@/components/ui/badge";
import { EmptyState, ErrorState, Skeleton } from "@/components/ui/states";
import { confirmer, toast } from "@/components/ui/feedback";
import { fmtDecimal1, fmtEntier, fmtEur0 } from "@/lib/format";
import { fmtDecimal1, fmtEntier, fmtEur2 } from "@/lib/format";
import { couleurMarge } from "@/lib/marge";
import { isStaleServerActionError, STALE_ACTION_MESSAGE } from "@/lib/stale-action";
@@ -228,7 +228,7 @@ export function SnapshotList({ type }: SnapshotListProps) {
<div>
<dt className="text-xs font-medium text-[var(--text-muted)]">Chiffre d&apos;affaires</dt>
<dd className="text-sm font-semibold tabular-nums text-[var(--text-primary)]">
{fmtEur0(resume.totalCa ?? 0)}
{fmtEur2(resume.totalCa ?? 0)}
</dd>
</div>
{taux != null && Number.isFinite(taux) && (