273 lines
7.7 KiB
TypeScript
273 lines
7.7 KiB
TypeScript
// // 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;
|
||
// daily_expected_hours: number;
|
||
// }[] = [];
|
||
|
||
// 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(
|
||
// `WARNING: 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,
|
||
// daily_expected_hours: 8,
|
||
// 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();
|
||
// });
|