mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-22 14:38:10 +02:00
Gammes de l'API à valider dans la Grille, analyses pleine largeur, référence dans Meilleures ventes
- API /api/v1 : PUT /products/:codein/gamme et POST /gammes déposent une proposition (table gammes_a_valider) au lieu d'enregistrer. La Grille la charge comme modification non enregistrée (badge « API ») : Enregistrer la valide, Annuler la rejette. Lecture : champ gammeAValider, statut a_valider dans la réponse ; OpenAPI mise à jour. - Meilleures ventes, Ventes par mois, Publicités : plus de largeur max centrée, les pages occupent toute la largeur. - Meilleures ventes : colonne Référence (référence fournisseur), recherche et export Excel compris. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FaniLFvegAZNXFrFFSStqa
This commit is contained in:
21 files changed
+413
-73
No files matched your search
@@ -141,7 +141,7 @@ export default async function AnalyticsPage(props: {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-screen-2xl">
|
||||
<div className="w-full min-w-0">
|
||||
<PageHeader
|
||||
icon={CalendarRange}
|
||||
title="Ventes par mois"
|
||||
|
||||
@@ -134,6 +134,14 @@ const COLONNES: DataColumn<Ligne>[] = [
|
||||
footer: () => "Total",
|
||||
className: "whitespace-nowrap",
|
||||
},
|
||||
{
|
||||
id: "reference",
|
||||
header: "Référence",
|
||||
hint: "Référence fournisseur de l'article",
|
||||
sortValue: r => r.reference,
|
||||
cell: r => (r.reference ? <span className="font-mono text-[13px] text-[var(--text-secondary)]">{r.reference}</span> : vide),
|
||||
className: "whitespace-nowrap",
|
||||
},
|
||||
{
|
||||
id: "libelle",
|
||||
header: "Désignation",
|
||||
@@ -170,7 +178,7 @@ const FILTRES: DataFilter<Ligne>[] = [
|
||||
{ id: "famille", label: "Famille", valueOf: famille, allLabel: "Toutes les familles" },
|
||||
];
|
||||
|
||||
const recherche = (r: Ligne) => [r.codein, r.libelle];
|
||||
const recherche = (r: Ligne) => [r.codein, r.reference, r.libelle];
|
||||
|
||||
interface Props {
|
||||
dateDebut: string;
|
||||
@@ -204,21 +212,22 @@ export function HitParadeClient({ dateDebut, dateFin, pivotted }: Props) {
|
||||
feuille: "Meilleures ventes",
|
||||
fichier: `meilleures-ventes_${dateDebut}_${dateFin}`,
|
||||
entetes: [
|
||||
"Code", "Désignation", "Fournisseur", "Nomenclature",
|
||||
"Code", "Référence", "Désignation", "Fournisseur", "Nomenclature",
|
||||
...GROUPES.flatMap(g => [
|
||||
`Qté ${nomExport(g)}`, `CA TTC ${nomExport(g)}`, `% Marge ${nomExport(g)}`, `Stock ${nomExport(g)}`,
|
||||
]),
|
||||
],
|
||||
largeurs: [12, 40, 25, 30, ...GROUPES.flatMap(() => [10, 14, 12, 10])],
|
||||
largeurs: [12, 18, 40, 25, 30, ...GROUPES.flatMap(() => [10, 14, 12, 10])],
|
||||
lignes: rows.map(r => [
|
||||
r.codein,
|
||||
r.reference,
|
||||
r.libelle.trim(),
|
||||
r.fournisseur,
|
||||
r.nomenclature_code ? `${r.nomenclature_code} — ${r.nomenclature}` : "",
|
||||
...GROUPES.flatMap(g => [r[g.qte], r[g.ca], pct(r[g.ca], r[g.marge]), r[g.stock]]),
|
||||
]),
|
||||
totaux: [
|
||||
`TOTAL — ${rows.length} articles`, "", "", "",
|
||||
`TOTAL — ${rows.length} articles`, "", "", "", "",
|
||||
...GROUPES.flatMap(g => {
|
||||
const ca = somme(rows, g.ca);
|
||||
return [somme(rows, g.qte), ca, pct(ca, somme(rows, g.marge)), somme(rows, g.stock)];
|
||||
@@ -266,7 +275,7 @@ export function HitParadeClient({ dateDebut, dateFin, pivotted }: Props) {
|
||||
columns={COLONNES}
|
||||
rowKey={r => r.codein}
|
||||
searchIn={recherche}
|
||||
searchPlaceholder="Code ou désignation…"
|
||||
searchPlaceholder="Code, référence ou désignation…"
|
||||
filters={FILTRES}
|
||||
pageSize={100}
|
||||
unite="produits"
|
||||
|
||||
@@ -13,6 +13,8 @@ export const metadata: Metadata = { title: "Meilleures ventes" };
|
||||
export interface HitParadePivotRow {
|
||||
codein: string;
|
||||
libelle: string;
|
||||
/** Référence fournisseur de l'article. */
|
||||
reference: string;
|
||||
fournisseur: string;
|
||||
nomenclature_code: string;
|
||||
nomenclature: string;
|
||||
@@ -38,6 +40,7 @@ function pivotHitParade(rows: HitParadeRow[]): HitParadePivotRow[] {
|
||||
map.set(row.codein, {
|
||||
codein: row.codein,
|
||||
libelle: row.libelle,
|
||||
reference: row.reference ?? "",
|
||||
fournisseur: row.fournisseur,
|
||||
nomenclature_code: row.nomenclature_code,
|
||||
nomenclature: row.nomenclature,
|
||||
@@ -100,7 +103,7 @@ export default async function HitParadePage(props: {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-screen-2xl">
|
||||
<div className="w-full min-w-0">
|
||||
<PageHeader
|
||||
icon={Trophy}
|
||||
title="Meilleures ventes"
|
||||
|
||||
@@ -120,7 +120,7 @@ export default async function PublicitesPage(props: {
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-screen-2xl">
|
||||
<div className="w-full min-w-0">
|
||||
<PageHeader
|
||||
icon={Megaphone}
|
||||
title="Publicités"
|
||||
|
||||
Reference in new issue
Block a user