feat: Add Drizzle ORM database setup, product data fetching and processing with scoring, and a diagnostic API endpoint.

This commit is contained in:
Michael committed 2026-03-10 16:04:55 +01:00
1 parent 1e5bb74344
commit 5eb3f6732e
3 files changed
+81 -84

No files matched your search

+2 -84
View File
@@ -17,90 +17,8 @@ interface GetProductRowsInput {
}
export async function getProductRows(input: GetProductRowsInput): Promise<ProductRow[]> {
const { codeFournisseur, magasin = "TOTAL", filters } = input;
// FILE LOGGING (portainer might be filtering console.log)
try {
const logPath = "/app/data/debug_check.txt";
const entry = `\n[${new Date().toISOString()}] getProductRows called for ${codeFournisseur}\n`;
fs.appendFileSync(logPath, entry);
} catch (e) {}
console.log(`\n>>> [getProductRows] RECEIVED CALL for supplier: ${codeFournisseur}, magasin: ${magasin}`);
console.log(`>>> [getProductRows] filters:`, JSON.stringify(filters));
try {
// Self-healing: Ensure codeGammeInit column exists and is populated
if (!isSchemaInitialized) {
try {
await db.execute(sql`
ALTER TABLE ventes_produits
ADD COLUMN IF NOT EXISTS code_gamme_init VARCHAR(20)
`);
await db.execute(sql`
UPDATE ventes_produits
SET code_gamme_init = code_gamme
WHERE code_gamme_init IS NULL
`);
isSchemaInitialized = true;
console.log("[Schema] codeGammeInit initialized successfully.");
} catch (err) {
console.error("[Schema] Failed to initialize codeGammeInit:", err);
// We continue anyway, the mapping fallback will handle it for the UI
}
}
// ... (allowedPeriods calculation remains the same)
const now = new Date();
const allowedPeriods = new Set<string>();
for (let i = 12; i >= 0; i--) { // i >= 0 au lieu de 1 pour inclure le mois en cours
const d = new Date(now.getFullYear(), now.getMonth() - i, 1);
allowedPeriods.add(`${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}`);
}
// Build conditions
const conditions = [
eq(ventesProduits.codeFournisseur, codeFournisseur),
ne(ventesProduits.periode, "TOTAL"),
magasin !== "TOTAL" ? eq(ventesProduits.magasin, magasin) : ne(ventesProduits.magasin, "TOTAL"),
];
// Hierarchical filtering
if (filters?.code3) {
conditions.push(eq(ventesProduits.code3, filters.code3));
} else if (filters?.code2) {
conditions.push(like(ventesProduits.code3, `${filters.code2}%`));
} else if (filters?.code1) {
conditions.push(like(ventesProduits.code3, `${filters.code1}%`));
}
// Get all rows for this supplier
const rows = await db
.select()
.from(ventesProduits)
.where(and(...conditions));
// Group by codein and aggregate monthly sales + track store participation
const productMap = new Map<string, ProductRow>();
// EXTRA VERBOSE DIAGNOSTIC
const countAll = await db.execute(sql`SELECT COUNT(*) FROM ventes_produits WHERE code_fournisseur = ${codeFournisseur}`);
const countDistinctTotal = await db.execute(sql`SELECT COUNT(DISTINCT codein) FROM ventes_produits WHERE code_fournisseur = ${codeFournisseur}`);
const countNoTotal = await db.execute(sql`SELECT COUNT(DISTINCT codein) FROM ventes_produits WHERE code_fournisseur = ${codeFournisseur} AND magasin != 'TOTAL' AND periode != 'TOTAL'`);
const diagOutput = `
--- [getProductRows] ULTRA-DIAGNOSTIC for Supplier ${codeFournisseur} ---
- Gross total rows in DB: ${countAll.rows[0].count}
- Total distinct products (any store/period): ${countDistinctTotal.rows[0].count}
- Distinct products excluding TOTAL store/period: ${countNoTotal.rows[0].count}
- Current Filters rows retrieved: ${rows.length}
- Distinct products in currently fetched rows: ${new Set(rows.map(r => r.codein)).size}
`;
try {
fs.appendFileSync("/app/data/debug_check.txt", diagOutput);
} catch (e) {}
console.log(diagOutput);
// MODEL DIAGNOSTIC (Clean version)
console.log(`[getProductRows] Fetching for ${codeFournisseur} (${magasin}). Found ${rows.length} rows.`);
const storeParticipationMap = new Map<string, Map<string, Set<string>>>(); // codein -> magasin -> Set of periods