feat(qlik): extract 4 decision metrics (CA/mag, couverture, marge%, rupture%)

Adds CA par Magasin, Couverture stock Qte, Marge % and Stock Rupture %
network measures to the Qlik hypercube extraction (in-page Playwright ws
+ legacy server ws), persists them in qlik_network_metrics, joins them in
getProductRows and renders 4 new grid columns with buy-signal coloring
(low coverage / high margin = green; high rupture = rose).

- qlik-client: config IDs (env-overridable, master GUIDs of app 9872ee6e),
  NetworkMetric fields, hypercube width 4->8, page height 2500->1250
- qlik-playwright: same 4 measures in the in-page hypercube + row mapping
- schema + db-init: 4 numeric columns (ALTER ADD COLUMN IF NOT EXISTS for
  existing DBs)
- cache + grid types + enrich: plumb the new fields end to end

Note: percent measures stored as raw Qlik ratio; UI renders x100 when |v|<=1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 4.8 committed 2026-06-21 08:34:54 +02:00
1 parent 0b9846f55d
commit bd9bc78f4c
8 files changed
+148 -12

No files matched your search

+16
View File
@@ -15,6 +15,10 @@ export interface NetworkMetricCached {
caReseau: number;
qteReseau: number;
nbMagasinsReseau: number;
caParMagasinReseau: number;
couvertureStockReseau: number;
margePctReseau: number;
rupturePctReseau: number;
fetchedAt: string | null;
}
@@ -36,6 +40,10 @@ export async function getNetworkMetricsByCodeCentrale(
caReseau: Number(r.caReseau ?? 0) || 0,
qteReseau: Number(r.qteReseau ?? 0) || 0,
nbMagasinsReseau: Number(r.nbMagasinsReseau ?? 0) || 0,
caParMagasinReseau: Number(r.caParMagasinReseau ?? 0) || 0,
couvertureStockReseau: Number(r.couvertureStockReseau ?? 0) || 0,
margePctReseau: Number(r.margePctReseau ?? 0) || 0,
rupturePctReseau: Number(r.rupturePctReseau ?? 0) || 0,
fetchedAt: r.fetchedAt ? new Date(r.fetchedAt).toISOString() : null,
});
}
@@ -51,6 +59,10 @@ export async function upsertNetworkMetrics(metrics: NetworkMetric[]): Promise<nu
caReseau: String(m.caReseau),
qteReseau: String(m.qteReseau),
nbMagasinsReseau: m.nbMagasinsReseau,
caParMagasinReseau: String(m.caParMagasinReseau),
couvertureStockReseau: String(m.couvertureStockReseau),
margePctReseau: String(m.margePctReseau),
rupturePctReseau: String(m.rupturePctReseau),
periode: m.periode ?? null,
fetchedAt: now,
}));
@@ -69,6 +81,10 @@ export async function upsertNetworkMetrics(metrics: NetworkMetric[]): Promise<nu
caReseau: sql`excluded.ca_reseau`,
qteReseau: sql`excluded.qte_reseau`,
nbMagasinsReseau: sql`excluded.nb_magasins_reseau`,
caParMagasinReseau: sql`excluded.ca_par_magasin_reseau`,
couvertureStockReseau: sql`excluded.couverture_stock_reseau`,
margePctReseau: sql`excluded.marge_pct_reseau`,
rupturePctReseau: sql`excluded.rupture_pct_reseau`,
periode: sql`excluded.periode`,
fetchedAt: sql`excluded.fetched_at`,
},