mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-12 09:46:56 +02:00
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>
80 lines
2.6 KiB
TypeScript
80 lines
2.6 KiB
TypeScript
/**
|
|
* 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;
|
|
}
|