targo-backend/scripts/done/import-employees-from-csv.ts

271 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// // 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<string, UserSummary>();
// 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 limport CSV → Employees', err);
// process.exit(1);
// })
// .finally(async () => {
// await prisma.$disconnect();
// });