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:
MichaelandClaude Opus 4.8 committed 2026-06-20 22:52:39 +02:00
1 parent 02a10caa4b
commit aee968d30f
34 files changed
+1043 -2925

No files matched your search

+19
View File
@@ -104,6 +104,25 @@ export const commandeCadences = pgTable("commande_cadences", {
];
});
/**
* Cache des métriques réseau Qlik Sense (~270 magasins La Foir'Fouille).
* Clé = code centrale (format 10000XXXXXX). Rafraîchi par /api/qlik/sync.
*/
export const qlikNetworkMetrics = pgTable("qlik_network_metrics", {
/** Code centrale article (clé jointure Qlik ↔ FF) */
codeCentrale: varchar("code_centrale", { length: 20 }).primaryKey(),
/** CA réseau total du produit */
caReseau: numeric("ca_reseau", { precision: 16, scale: 2 }),
/** Quantité vendue réseau */
qteReseau: numeric("qte_reseau", { precision: 14, scale: 2 }),
/** Nombre de magasins travaillant le produit (sur ~270) */
nbMagasinsReseau: integer("nb_magasins_reseau"),
/** Période couverte (libre, ex "12m" ou "2025") */
periode: varchar("periode", { length: 20 }),
/** Dernière synchro depuis Qlik */
fetchedAt: timestamp("fetched_at").defaultNow(),
});
/** AI Context rules per supplier (Epic: AI Context) */
export const aiSupplierContext = pgTable("ai_supplier_context", {
/** Supplier code serving as the primary key */