// // src/scripts/import-employees-from-csv.ts // import { PrismaClient, Roles } from '@prisma/client'; // import * as fs from 'fs'; // import * as path from 'path'; // const prisma = new PrismaClient(); // // ⚙️ Chemin vers ton CSV employees // const CSV_PATH = path.resolve(__dirname, '../../data/export_new_employee_table.csv'); // // Rôles éligibles pour la table Employees // const ELIGIBLE_ROLES: Roles[] = [ // Roles.EMPLOYEE, // Roles.SUPERVISOR, // Roles.HR, // Roles.ACCOUNTING, // Roles.ADMIN, // ]; // // Type correspondant EXACT aux colonnes de ton CSV // type EmployeeCsvRow = { // employee_number: string; // email: string; // job_title: string; // company: string; // sera converti en number // is_supervisor: string; // "True"/"False" (ou variantes) // onboarding: string; // millis // offboarding: string; // millis ou "NULL" // }; // // Représentation minimale d'un user // type UserSummary = { // id: string; // UUID // email: string; // role: Roles; // }; // // ============ Helpers CSV ============ // function splitCsvLine(line: string): string[] { // const result: string[] = []; // let current = ''; // let inQuotes = false; // for (let i = 0; i < line.length; i++) { // const char = line[i]; // if (char === '"') { // // guillemet échappé "" // if (inQuotes && line[i + 1] === '"') { // current += '"'; // i++; // } else { // inQuotes = !inQuotes; // } // } else if (char === ',' && !inQuotes) { // result.push(current); // current = ''; // } else { // current += char; // } // } // result.push(current); // return result.map((v) => v.trim()); // } // // ============ Helpers de parsing ============ // function parseBoolean(value: string): boolean { // const v = value.trim().toLowerCase(); // return v === 'true' || v === '1' || v === 'yes' || v === 'y' || v === 'oui'; // } // function parseIntSafe(value: string, fieldName: string): number | null { // const trimmed = value.trim(); // if (!trimmed || trimmed.toUpperCase() === 'NULL') return null; // const n = Number.parseInt(trimmed, 10); // if (Number.isNaN(n)) { // console.warn(`⚠️ Impossible de parser "${value}" en entier pour le champ ${fieldName}`); // return null; // } // return n; // } // function millisToDate(value: string): Date | null { // const trimmed = value.trim().toUpperCase(); // if (!trimmed || trimmed === 'NULL') return null; // const ms = Number(trimmed); // if (!Number.isFinite(ms)) { // console.warn(`⚠️ Impossible de parser "${value}" en millis pour une Date`); // return null; // } // const d = new Date(ms); // // On normalise au jour (minuit UTC) // const normalized = new Date(Date.UTC( // d.getUTCFullYear(), // d.getUTCMonth(), // d.getUTCDate(), // )); // return normalized; // } // // ============ MAIN ============ // async function main() { // // 1. Lecture du CSV // const fileContent = fs.readFileSync(CSV_PATH, 'utf-8'); // const lines = fileContent // .split(/\r?\n/) // .map((l) => l.trim()) // .filter((l) => l.length > 0); // if (lines.length <= 1) { // console.error('CSV vide ou seulement un header'); // return; // } // const header = splitCsvLine(lines[0]); // ["employee_number","email",...] // const dataLines = lines.slice(1); // const csvRows: EmployeeCsvRow[] = dataLines.map((line) => { // const values = splitCsvLine(line); // const row: any = {}; // header.forEach((col, idx) => { // row[col] = values[idx] ?? ''; // }); // return row as EmployeeCsvRow; // }); // console.log(`➡️ ${csvRows.length} lignes trouvées dans le CSV employees`); // // 2. Récupérer tous les emails du CSV // const emails = Array.from( // new Set( // csvRows // .map((r) => r.email.trim()) // .filter((e) => e.length > 0), // ), // ); // console.log(`➡️ ${emails.length} emails uniques trouvés dans le CSV`); // // 3. Charger les users correspondants avec les bons rôles // const users = (await prisma.users.findMany({ // where: { // email: { in: emails }, // role: { in: ELIGIBLE_ROLES }, // }, // select: { // id: true, // email: true, // role: true, // }, // })) as UserSummary[]; // console.log(`➡️ ${users.length} users éligibles trouvés dans la DB`); // // Map email → user // const userByEmail = new Map(); // for (const user of users) { // const key = user.email.trim().toLowerCase(); // userByEmail.set(key, user); // } // // 4. Construire les données pour employees.createMany // const employeesToCreate: { // user_id: string; // external_payroll_id: number; // company_code: number; // first_work_day: Date; // last_work_day: Date | null; // job_title: string | null; // is_supervisor: boolean; // supervisor_id?: number | null; // }[] = []; // const rowsWithoutUser: EmployeeCsvRow[] = []; // const rowsWithInvalidNumbers: EmployeeCsvRow[] = []; // for (const row of csvRows) { // const emailKey = row.email.trim().toLowerCase(); // const user = userByEmail.get(emailKey); // if (!user) { // rowsWithoutUser.push(row); // continue; // } // const external_payroll_id = parseIntSafe(row.employee_number, 'external_payroll_id'); // const company_code = parseIntSafe(String(row.company), 'company_code'); // if (external_payroll_id === null || company_code === null) { // rowsWithInvalidNumbers.push(row); // continue; // } // const first_work_day = millisToDate(row.onboarding); // const last_work_day = millisToDate(row.offboarding); // const is_supervisor = parseBoolean(row.is_supervisor); // const job_title = row.job_title?.trim() || null; // if (!first_work_day) { // console.warn( // `⚠️ Date d'onboarding invalide pour ${row.email} (employee_number=${row.employee_number})`, // ); // continue; // } // employeesToCreate.push({ // user_id: user.id, // external_payroll_id, // company_code, // first_work_day, // last_work_day, // job_title, // is_supervisor, // supervisor_id: null, // on pourra gérer ça plus tard si tu as les infos // }); // } // console.log(`➡️ ${employeesToCreate.length} entrées Employees prêtes à être insérées`); // if (rowsWithoutUser.length > 0) { // console.warn(`⚠️ ${rowsWithoutUser.length} lignes CSV sans user correspondant (email / rôle) :`); // for (const row of rowsWithoutUser) { // console.warn( // ` - email=${row.email}, employee_number=${row.employee_number}, company=${row.company}`, // ); // } // } // if (rowsWithInvalidNumbers.length > 0) { // console.warn(`⚠️ ${rowsWithInvalidNumbers.length} lignes CSV avec ids/compagnies invalides :`); // for (const row of rowsWithInvalidNumbers) { // console.warn( // ` - email=${row.email}, employee_number="${row.employee_number}", company="${row.company}"`, // ); // } // } // if (employeesToCreate.length === 0) { // console.warn('⚠️ Aucun Employees à créer, arrêt.'); // return; // } // // 5. Insert en batch // const result = await prisma.employees.createMany({ // data: employeesToCreate, // skipDuplicates: true, // évite les erreurs si tu relances le script // }); // console.log(`✅ ${result.count} employees insérés dans la DB`); // } // main() // .catch((err) => { // console.error('❌ Erreur pendant l’import CSV → Employees', err); // process.exit(1); // }) // .finally(async () => { // await prisma.$disconnect(); // });