feat: add analytics page with CA TTC comparison by supplier/nomenclature

- pg-ff-client: add pgGetCaByFournisseur() and pgGetCaByNomenclature()
  returning CA TTC per site and month for monthly N vs N-1 comparison
- analytics/page.tsx: server component with pivot logic to reshape raw
  query data into store-separated columns (292/579/total) with evolution %
- analytics/client.tsx: client controls (mode dropdown, month selector)
  and responsive table with color-coded evolution badges
- sidebar: add "Analytics" link to navigation

Features:
- Switch between "Fournisseur" and "Nomenclature" views
- Select any month for comparison vs same month prior year
- Columns split by store: Frouard/Nancy (292), Houdemont (579), Total
- Each store shows: CA this month, CA N-1, evolution %
- Footer totals row matching sum of detail rows
- FR locale currency formatting (€)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Sonnet 4.6 committed 2026-03-31 09:40:31 +02:00
1 parent 7a30783285
commit 740aed3181
4 files changed
+483

No files matched your search

+77
View File
@@ -616,3 +616,80 @@ export async function pgGetDashboardData(): Promise<DashboardData> {
top10BySite,
};
}
// ---------------------------------------------------------------------------
// Analytics — CA TTC par fournisseur et nomenclature
// ---------------------------------------------------------------------------
export interface CaByFournisseurRow {
code: string;
nom: string;
site: string;
mois: string;
ca_ttc: number;
}
export interface CaByNomenclatureRow {
code: string;
libelle: string;
site: string;
mois: string;
ca_ttc: number;
}
/**
* Retourne le CA TTC par fournisseur et site pour deux mois donnés (mois et mois N-1).
*/
export async function pgGetCaByFournisseur(
mois: string,
moisN1: string
): Promise<CaByFournisseurRow[]> {
const result = await pgNoParallel(sql`
SELECT
af.code AS code,
MAX(f.raisonsociale)::text AS nom,
m.site,
TO_CHAR(m.datmvt, 'YYYY-MM') AS mois,
SUM(-m.mntmvtttc)::float AS ca_ttc
FROM mvtart m
JOIN articles a ON a.no_id = m.artnoid
JOIN artfou1 af ON af.art_no_id = a.no_id
JOIN fouadr1 f ON f.code = af.code AND f.sit_code = '000'
WHERE TO_CHAR(m.datmvt, 'YYYY-MM') = ANY(ARRAY[${mois}, ${moisN1}])
AND m.site IN ('292', '579')
AND m.genremvt = 3
GROUP BY af.code, m.site, TO_CHAR(m.datmvt, 'YYYY-MM')
ORDER BY af.code, m.site, mois
`);
console.log(`[pg-ff] CaByFournisseur: ${result.rows.length} lignes pour ${mois}/${moisN1}`);
return result.rows as unknown as CaByFournisseurRow[];
}
/**
* Retourne le CA TTC par nomenclature (code3) et site pour deux mois donnés.
*/
export async function pgGetCaByNomenclature(
mois: string,
moisN1: string
): Promise<CaByNomenclatureRow[]> {
const result = await pgNoParallel(sql`
SELECT
n.code AS code,
n.libelle::text AS libelle,
m.site,
TO_CHAR(m.datmvt, 'YYYY-MM') AS mois,
SUM(-m.mntmvtttc)::float AS ca_ttc
FROM mvtart m
JOIN articles a ON a.no_id = m.artnoid
JOIN nomenclature n ON n.no_id = a.nom_no_id
WHERE TO_CHAR(m.datmvt, 'YYYY-MM') = ANY(ARRAY[${mois}, ${moisN1}])
AND m.site IN ('292', '579')
AND m.genremvt = 3
GROUP BY n.code, n.libelle, m.site, TO_CHAR(m.datmvt, 'YYYY-MM')
ORDER BY n.code, m.site, mois
`);
console.log(`[pg-ff] CaByNomenclature: ${result.rows.length} lignes pour ${mois}/${moisN1}`);
return result.rows as unknown as CaByNomenclatureRow[];
}