mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-17 12:19:50 +02:00
feat(grid): replace ranking/AI/score with Qlik network data
Remove ranking, AI analysis (routes + ai-copilot module), and score-engine from the grid. Add Qlik Sense network metrics (CA, Qte, nb magasins per product) joined by code centrale (articles.artcentrale). - qlik-client.ts: NTLM + ticket SSO auth, QIX hypercube extraction (master items) - qlik-network-cache.ts + qlik_network_metrics table (db-init): cache layer - POST /api/qlik/sync?fournisseur=: per-supplier sync into cache - SyncQlikButton: per-supplier sync, grid reload, last-update date display - get-product-rows Phase 8: enrich rows with cached network metrics - heatmap-grid: CA reseau / Qte reseau / Magasins (/270) / % presence columns - discovery: app 9872ee6e, master items CA N / Quantite N / Magasin Ventes Nb N - deps: httpntlm, ws Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
02a10caa4b
commit
aee968d30f
34 files changed
+1043
-2925
No files matched your search
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* CollectFlow — Cache des metriques reseau Qlik (table qlik_network_metrics).
|
||||
*
|
||||
* Lecture par lot (jointure grille) + upsert masse (route de sync).
|
||||
*/
|
||||
|
||||
import "server-only";
|
||||
|
||||
import { db } from "@/db";
|
||||
import { qlikNetworkMetrics } from "@/db/schema";
|
||||
import { inArray, sql } from "drizzle-orm";
|
||||
import type { NetworkMetric } from "@/lib/qlik-client";
|
||||
|
||||
export interface NetworkMetricCached {
|
||||
caReseau: number;
|
||||
qteReseau: number;
|
||||
nbMagasinsReseau: number;
|
||||
fetchedAt: string | null;
|
||||
}
|
||||
|
||||
/** Lit les metriques reseau pour une liste de codes centraux. */
|
||||
export async function getNetworkMetricsByCodeCentrale(
|
||||
codes: string[],
|
||||
): Promise<Map<string, NetworkMetricCached>> {
|
||||
const out = new Map<string, NetworkMetricCached>();
|
||||
const unique = [...new Set(codes.filter(Boolean))];
|
||||
if (unique.length === 0) return out;
|
||||
|
||||
const rows = await db
|
||||
.select()
|
||||
.from(qlikNetworkMetrics)
|
||||
.where(inArray(qlikNetworkMetrics.codeCentrale, unique));
|
||||
|
||||
for (const r of rows) {
|
||||
out.set(r.codeCentrale, {
|
||||
caReseau: Number(r.caReseau ?? 0) || 0,
|
||||
qteReseau: Number(r.qteReseau ?? 0) || 0,
|
||||
nbMagasinsReseau: Number(r.nbMagasinsReseau ?? 0) || 0,
|
||||
fetchedAt: r.fetchedAt ? new Date(r.fetchedAt).toISOString() : null,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Upsert en masse des metriques reseau (depuis Qlik). */
|
||||
export async function upsertNetworkMetrics(metrics: NetworkMetric[]): Promise<number> {
|
||||
if (metrics.length === 0) return 0;
|
||||
const now = new Date();
|
||||
const values = metrics.map((m) => ({
|
||||
codeCentrale: m.codeCentrale,
|
||||
caReseau: String(m.caReseau),
|
||||
qteReseau: String(m.qteReseau),
|
||||
nbMagasinsReseau: m.nbMagasinsReseau,
|
||||
periode: m.periode ?? null,
|
||||
fetchedAt: now,
|
||||
}));
|
||||
|
||||
// Upsert par paquets pour eviter les requetes trop volumineuses.
|
||||
const chunk = 500;
|
||||
let count = 0;
|
||||
for (let i = 0; i < values.length; i += chunk) {
|
||||
const batch = values.slice(i, i + chunk);
|
||||
await db
|
||||
.insert(qlikNetworkMetrics)
|
||||
.values(batch)
|
||||
.onConflictDoUpdate({
|
||||
target: qlikNetworkMetrics.codeCentrale,
|
||||
set: {
|
||||
caReseau: sql`excluded.ca_reseau`,
|
||||
qteReseau: sql`excluded.qte_reseau`,
|
||||
nbMagasinsReseau: sql`excluded.nb_magasins_reseau`,
|
||||
periode: sql`excluded.periode`,
|
||||
fetchedAt: sql`excluded.fetched_at`,
|
||||
},
|
||||
});
|
||||
count += batch.length;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
Reference in new issue
Block a user