Files
CollectFlow/src/types/grid.ts
T
Claude 71563500a0 feat(grille): tendance réseau (sparkline) + modal ventes réseau par mois
Le sync Qlik calculait déjà les ventes réseau mois par mois mais les
sommait en un total. On conserve désormais le détail mensuel.

- Sync : accumulation additive qteByMonth { code: { 'YYYY-MM': qté } }
  in-page (chemins rapide + par lots), rattachée au métrique réseau.
  Reset propre en cas de repli code 15. Total inchangé.
- Stockage : colonne jsonb qte_by_month sur qlik_network_metrics
  (schema + db-init ALTER), upsert + lecture.
- ProductRow.qteReseauByMonth propagé jusqu'à la grille.
- UI grille :
  · colonne '% Présence réseau' remplacée par 'Tendance réseau' :
    sparkline 12 mois teintée + flèche + variation % (moyenne 4 derniers
    mois vs 4 précédents), triable ;
  · cellule 'Nb magasins' rendue cliquable → modal des ventes réseau
    mois par mois (barres, 12 derniers mois).

Nécessite un nouveau Sync Qlik pour peupler le détail mensuel (les
données déjà en cache n'ont que le total → modal 'pas encore de détail').
2026-07-22 14:17:57 +00:00

148 lines
5.5 KiB
TypeScript

/**
* CollectFlow - Core Grid Types
*/
/** One row in the data grid, representing a product at a store for one period. */
export interface VenteProduit {
id: number;
codein: string;
codeFournisseur: string | null;
nomFournisseur: string | null;
libelle1: string | null;
gtin: string | null;
reference: string | null;
codeGamme: GammeCode | null;
codeGammeInit: GammeCode | null;
code3: string | null;
libelle3: string | null;
magasin: string;
codeMagasin: string | null;
annee: number | null;
mois: number | null;
periode: string;
quantite: number | null;
montantMvt: number | null;
margeMvt: number | null;
}
/** A/B/C/Z status codes for product categories, but can be any string from DB (like 'Aucune') */
export type GammeCode = string;
/** A product row with its 12-month sales history and computed KPIs */
export interface ProductRow {
codein: string;
codeFournisseur: string;
nomFournisseur: string;
/** Dernier fournisseur enregistré pour cet article (artfou1.no_id DESC). Peut différer du fournisseur consulté. */
fournisseurPrincipalCode?: string;
fournisseurPrincipalNom?: string;
libelle1: string;
gtin: string;
reference: string;
code1: string;
libelleNiveau1: string;
code2: string;
libelleNiveau2: string;
code3: string;
libelle3: string;
codeGamme: GammeCode | null;
codeGammeInit: GammeCode | null;
/** Draft gamme value before saving */
codeGammeDraft: GammeCode | null;
/** 12-month sales quantities, indexed by YYYYMM key */
sales12m: Record<string, number>;
/** End-of-month stock levels, indexed by YYYYMM key */
stock12m: Record<string, number>;
/** Monthly reception quantities, indexed by YYYYMM key */
receptions12m: Record<string, number>;
/** Per-store breakdown (site → YYYYMM → qty) — always present even in TOTAL mode */
sales12mByStore?: Record<string, Record<string, number>>;
/** Per-store stock breakdown (site → YYYYMM → stock fin de mois) */
stock12mByStore?: Record<string, Record<string, number>>;
/** Per-store reception breakdown (site → YYYYMM → qté reçue) */
receptions12mByStore?: Record<string, Record<string, number>>;
/** Per-store last delivery/reception date (site → ISO date string) */
derniereLivraisonByStore?: Record<string, string>;
/** Per-store CA totals */
caByStore?: Record<string, number>;
/** Per-store quantity totals */
quantiteByStore?: Record<string, number>;
/** Per-store margin totals */
margeByStore?: Record<string, number>;
totalQuantite: number;
totalCa: number;
totalMarge: number;
tauxMarge: number;
workingStores: string[];
/** Rotation normalisée : unités vendues / magasin / mois */
unitsPerStorePerMonth?: number;
/** CA normalisé : € / magasin / an */
caPerStorePerYear?: number;
/** Produit récent (≤ 3 mois de données) */
isRecent?: boolean;
/** Seul produit du fournisseur dans le lot */
isLastProduct?: boolean;
/** Top 30% CA du lot fournisseur */
isTop30Supplier?: boolean;
/** SQL Server internal ID — nécessaire pour /api/articles/:noid/mensuel */
noid?: number;
/** Données stock & approvisionnement (API FF Nancy) */
pcb?: number; // Conditionnement (pack size)
prixAchat?: number; // PA / PRMP
prixVente?: number; // PV central
stockActuel?: number; // Stock dispo agrégé tous sites
stockTotal?: number; // Qte en stock agrégé
stockValeur?: number; // Valeur stock agrégée
pa?: number; // PRMP (alias prixAchat)
derniereVente?: string; // ISO date de dernière vente
derniereLivraison?: string; // ISO date de dernière réception
nbJoursDerniereVente?: number; // Nb jours sans vente
commandesEnCours?: number; // Qté en commande fournisseur
/** Moyennes de comparaison pour l'IA */
avgQtyFournisseur?: number;
avgQtyRayon?: number;
/** Poids relatifs (%) du produit chez le fournisseur */
shareCa?: number;
shareMarge?: number;
shareQty?: number;
/** Référentiels globaux */
totalFournisseurCa?: number;
/** Code centrale (clé jointure Qlik, format 10000XXXXXX) */
codeCentrale?: string;
/** Données réseau Qlik (~270 magasins) */
caReseau?: number;
qteReseau?: number;
nbMagasinsReseau?: number;
/** CA moyen par magasin réseau (productivité normalisée présence) */
caParMagasinReseau?: number;
/** Taux de marge réseau (ratio brut Qlik, ex 0.32 = 32%) */
margePctReseau?: number;
/** Taux de présence réseau = nbMagasinsReseau / 270 */
tauxPresenceReseau?: number;
/** Quantité vendue réseau par mois : { "YYYY-MM": qté } (tendance / modal). */
qteReseauByMonth?: Record<string, number> | null;
/** Fraîcheur des données réseau (ISO) */
networkFetchedAt?: string;
}
/** Summary bar totals for the currently visible/filtered rows */
export interface GridSummary {
totalProducts: number;
totalRows: number;
totalQuantite: number;
totalCa: number;
totalMarge: number;
tauxMargeGlobal: number;
}
/** Filters applied to the grid */
export interface GridFilters {
magasin: string | null;
codeFournisseur: string | null;
code1: string | null;
code2: string | null;
code3: string | null;
codeGamme: GammeCode | null;
search: string;
}