diff --git a/prisma/mock-seeds-scripts/02-users.ts b/prisma/mock-seeds-scripts/02-users.ts index 0352eb9..04ec7e4 100644 --- a/prisma/mock-seeds-scripts/02-users.ts +++ b/prisma/mock-seeds-scripts/02-users.ts @@ -24,15 +24,19 @@ async function main() { const pick = (arr: T[]) => arr[Math.floor(Math.random() * arr.length)]; + // 40 employees, avec une distribution initiale const rolesForEmployees: Roles[] = [ Roles.ADMIN, - ...Array(4).fill(Roles.SUPERVISOR), + ...Array(4).fill(Roles.SUPERVISOR), // 4 superviseurs Roles.HR, Roles.ACCOUNTING, ...Array(33).fill(Roles.EMPLOYEE), ]; - // 40 employees + // --- Normalisation : forcer user5@example.test à SUPERVISOR --- + // user5 => index 4 (i = 4) + rolesForEmployees[4] = Roles.SUPERVISOR; + for (let i = 0; i < 40; i++) { const fn = pick(firstNames); const ln = pick(lastNames); @@ -40,8 +44,7 @@ async function main() { first_name: fn, last_name: ln, email: emailFor(i), - // on concatène proprement en string - phone_number: BASE_PHONE + i.toString(), + phone_number: BASE_PHONE + i.toString(), residence: Math.random() < 0.5 ? 'QC' : 'ON', role: rolesForEmployees[i], }); @@ -61,8 +64,29 @@ async function main() { }); } + // 1) Insert (sans doublons) await prisma.users.createMany({ data: usersData, skipDuplicates: true }); - console.log('✓ Users: 50 rows (40 employees, 10 customers)'); + + // 2) Validation/Correction post-insert : + // - garantir que user5@example.test est SUPERVISOR + // - si jamais le projet avait un user avec la typo, on tente aussi de le corriger (fallback) + const targetEmails = ['user5@example.test', 'user5@examplte.tset']; + for (const email of targetEmails) { + try { + await prisma.users.update({ + where: { email }, + data: { role: Roles.SUPERVISOR }, + }); + console.log(`✓ Validation: ${email} est SUPERVISOR`); + break; // on s'arrête dès qu'on a corrigé l'un des deux + } catch { + // ignore si non trouvé, on tente l'autre + } + } + + // 3) Petite vérif : compter les superviseurs pour sanity check + const supCount = await prisma.users.count({ where: { role: Roles.SUPERVISOR } }); + console.log(`✓ Users: 50 rows (40 employees, 10 customers) — SUPERVISORS: ${supCount}`); } main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/03-employees.ts b/prisma/mock-seeds-scripts/03-employees.ts index 3076683..7377971 100644 --- a/prisma/mock-seeds-scripts/03-employees.ts +++ b/prisma/mock-seeds-scripts/03-employees.ts @@ -12,7 +12,7 @@ function randomPastDate(yearsBack = 3) { past.setFullYear(now.getFullYear() - yearsBack); const t = randInt(past.getTime(), now.getTime()); const d = new Date(t); - d.setHours(0,0,0,0); + d.setHours(0, 0, 0, 0); return d; } @@ -29,7 +29,12 @@ const jobTitles = [ ]; function randomTitle() { - return jobTitles[randInt(0, jobTitles.length -1)]; + return jobTitles[randInt(0, jobTitles.length - 1)]; +} + +// Sélection aléatoire entre 271583 et 271585 +function randomCompanyCode() { + return Math.random() < 0.5 ? 271583 : 271585; } async function main() { @@ -38,40 +43,41 @@ async function main() { orderBy: { email: 'asc' }, }); - // Create supervisors first - const supervisorUsers = employeeUsers.filter(u => u.role === Roles.SUPERVISOR); - const supervisorEmployeeIds: number[] = []; - - for (const u of supervisorUsers) { - const emp = await prisma.employees.upsert({ - where: { user_id: u.id }, - update: {}, - create: { - user_id: u.id, - external_payroll_id: randInt(10000, 99999), - company_code: randInt(1, 5), - first_work_day: randomPastDate(3), - last_work_day: null, - job_title: randomTitle(), - is_supervisor: true, - }, - }); - supervisorEmployeeIds.push(emp.id); + // 1) Trouver le user qui sera le superviseur fixe + const supervisorUser = await prisma.users.findUnique({ + where: { email: 'user5@examplte.test' }, + }); + if (!supervisorUser) { + throw new Error("Le user 'user5@examplte.test' n'existe pas !"); } - // Create remaining employees, assign a random supervisor (admin can have none) + // 2) Créer ou récupérer son employee avec is_supervisor = true + const supervisorEmp = await prisma.employees.upsert({ + where: { user_id: supervisorUser.id }, + update: { is_supervisor: true }, + create: { + user_id: supervisorUser.id, + external_payroll_id: randInt(10000, 99999), + company_code: randomCompanyCode(), + first_work_day: randomPastDate(3), + last_work_day: null, + job_title: randomTitle(), + is_supervisor: true, + }, + }); + + // 3) Créer tous les autres employés avec ce superviseur (sauf ADMIN qui n’a pas de superviseur) for (const u of employeeUsers) { const already = await prisma.employees.findUnique({ where: { user_id: u.id } }); if (already) continue; - const supervisor_id = - u.role === Roles.ADMIN ? null : supervisorEmployeeIds[randInt(0, supervisorEmployeeIds.length - 1)]; + const supervisor_id = u.role === Roles.ADMIN ? null : supervisorEmp.id; await prisma.employees.create({ data: { user_id: u.id, external_payroll_id: randInt(10000, 99999), - company_code: randInt(1, 5), + company_code: randomCompanyCode(), first_work_day: randomPastDate(3), last_work_day: null, supervisor_id, @@ -81,7 +87,7 @@ async function main() { } const total = await prisma.employees.count(); - console.log(`✓ Employees: ${total} rows (with supervisors linked)`); + console.log(`✓ Employees: ${total} rows (supervisor = ${supervisorUser.email})`); } main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/10-shifts.ts b/prisma/mock-seeds-scripts/10-shifts.ts index fc6c7a4..dd89b47 100644 --- a/prisma/mock-seeds-scripts/10-shifts.ts +++ b/prisma/mock-seeds-scripts/10-shifts.ts @@ -3,25 +3,22 @@ import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); // ====== Config ====== -const PREVIOUS_WEEKS = 5; // nombre de semaines à générer avant la semaine actuelle -const INCLUDE_CURRENT = false; // passe à true si tu veux aussi générer la semaine actuelle +const PREVIOUS_WEEKS = 5; +const INCLUDE_CURRENT = false; -// Stocker une heure (Postgres TIME) via Date (UTC 1970-01-01) function timeAt(hour: number, minute: number) { return new Date(Date.UTC(1970, 0, 1, hour, minute, 0)); } -// Lundi de la semaine (en UTC) pour la date courante function mondayOfThisWeekUTC(now = new Date()) { const d = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())); - const day = d.getUTCDay(); // 0=Dim, 1=Lun, ... - const diffToMonday = (day + 6) % 7; // 0 si lundi + const day = d.getUTCDay(); + const diffToMonday = (day + 6) % 7; d.setUTCDate(d.getUTCDate() - diffToMonday); d.setUTCHours(0, 0, 0, 0); return d; } -// Retourne les 5 dates Lundi→Vendredi (UTC, à minuit) à partir d’un lundi donné function weekDatesFromMonday(monday: Date) { return Array.from({ length: 5 }, (_, i) => { const d = new Date(monday); @@ -30,21 +27,19 @@ function weekDatesFromMonday(monday: Date) { }); } -// Lundi n semaines avant un lundi donné function mondayNWeeksBefore(monday: Date, n: number) { const d = new Date(monday); d.setUTCDate(d.getUTCDate() - n * 7); return d; } -// Random int inclusif function rndInt(min: number, max: number) { return Math.floor(Math.random() * (max - min + 1)) + min; } async function main() { // Bank codes utilisés - const BANKS = ['G1', 'G305', 'G105'] as const; + const BANKS = ['G1', 'G56', 'G48', 'G700', 'G105', 'G305', 'G43'] as const; const bcRows = await prisma.bankCodes.findMany({ where: { bank_code: { in: BANKS as unknown as string[] } }, select: { id: true, bank_code: true }, @@ -54,12 +49,12 @@ async function main() { if (!bcMap.has(c)) throw new Error(`Bank code manquant: ${c}`); } - // Employés + cache des timesheets par employé (évite un findMany dans les boucles) const employees = await prisma.employees.findMany({ select: { id: true } }); if (!employees.length) { console.log('Aucun employé — rien à insérer.'); return; } + const tsByEmp = new Map(); { const allTs = await prisma.timesheets.findMany({ @@ -72,7 +67,6 @@ async function main() { } } - // Construit la liste des semaines à insérer const mondayThisWeek = mondayOfThisWeekUTC(); const mondays: Date[] = []; @@ -83,7 +77,6 @@ async function main() { let created = 0; - // Pour chaque semaine à générer for (let wi = 0; wi < mondays.length; wi++) { const monday = mondays[wi]; const weekDays = weekDatesFromMonday(monday); @@ -93,34 +86,22 @@ async function main() { const tss = tsByEmp.get(e.id) ?? []; if (!tss.length) continue; - // Base horaire spécifique à l’employé (garantit la diversité) - // Heures: 6..10 (selon l'index employé) - const baseStartHour = 6 + (ei % 5); // 6,7,8,9,10 - // Minutes: 0, 15, 30, 45 (selon l'index employé) - const baseStartMinute = (ei * 15) % 60; // 0,15,30,45 (répète) + const baseStartHour = 6 + (ei % 5); + const baseStartMinute = (ei * 15) % 60; - // 1 employé sur 5 a un jour spécial (G305/G105) par semaine - const isSpecial = (ei % 5) === 0; - const specialDayIdx = isSpecial ? ((ei + wi) % 5) : -1; - const specialCode = isSpecial ? ((ei + wi) % 2 === 0 ? 'G305' : 'G105') : 'G1'; - - // 5 jours (lun→ven) for (let di = 0; di < weekDays.length; di++) { const date = weekDays[di]; - // Bank code du jour - const codeToday = (di === specialDayIdx) ? specialCode : 'G1'; - const bank_code_id = bcMap.get(codeToday)!; + // Tirage aléatoire du bank_code + const randomCode = BANKS[Math.floor(Math.random() * BANKS.length)]; + const bank_code_id = bcMap.get(randomCode)!; - // Durée aléatoire entre 4 et 10 heures const duration = rndInt(4, 10); - // Variation jour+semaine pour casser les patterns (décalage 0..2h) - const dayWeekOffset = (di + wi + (ei % 3)) % 3; // 0,1,2 - const startH = Math.min(12, baseStartHour + dayWeekOffset); // borne supérieure prudente + const dayWeekOffset = (di + wi + (ei % 3)) % 3; + const startH = Math.min(12, baseStartHour + dayWeekOffset); const startM = baseStartMinute; - - const endH = startH + duration; // <= 22 en pratique + const endH = startH + duration; const endM = startM; const ts = tss[(di + wi) % tss.length]; @@ -129,8 +110,8 @@ async function main() { data: { timesheet_id: ts.id, bank_code_id, - description: `Shift ${di + 1} (semaine du ${monday.toISOString().slice(0,10)}) emp ${e.id} — ${codeToday}`, - date, // Date du jour (UTC minuit) + description: `Shift ${di + 1} (semaine du ${monday.toISOString().slice(0, 10)}) emp ${e.id} — ${randomCode}`, + date, start_time: timeAt(startH, startM), end_time: timeAt(endH, endM), is_approved: Math.random() < 0.5, diff --git a/prisma/mock-seeds-scripts/12-expenses.ts b/prisma/mock-seeds-scripts/12-expenses.ts index 177e971..c6fd430 100644 --- a/prisma/mock-seeds-scripts/12-expenses.ts +++ b/prisma/mock-seeds-scripts/12-expenses.ts @@ -2,7 +2,7 @@ import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); -// Lundi de la semaine (en UTC) pour la date courante +// Lundi (UTC) de la semaine courante function mondayOfThisWeekUTC(now = new Date()) { const d = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())); const day = d.getUTCDay(); // 0=Dim, 1=Lun, ... @@ -12,7 +12,7 @@ function mondayOfThisWeekUTC(now = new Date()) { return d; } -// Retourne les 5 dates Lundi→Vendredi (UTC, à minuit) +// Dates Lundi→Vendredi (UTC minuit) function currentWeekDates() { const monday = mondayOfThisWeekUTC(); return Array.from({ length: 5 }, (_, i) => { @@ -27,19 +27,19 @@ function rndInt(min: number, max: number) { } function rndAmount(minCents: number, maxCents: number) { const cents = rndInt(minCents, maxCents); - return (cents / 100).toFixed(2); // string (ex: "123.45") + return (cents / 100).toFixed(2); // string "123.45" } async function main() { - // On veut explicitement G503 (mileage) et G517 (remboursement) - const wanted = ['G57', 'G517'] as const; - const codes = await prisma.bankCodes.findMany({ - where: { bank_code: { in: wanted as unknown as string[] } }, + // Codes autorisés (aléatoires à chaque dépense) + const BANKS = ['G517', 'G57', 'G502', 'G202', 'G234'] as const; + const bcRows = await prisma.bankCodes.findMany({ + where: { bank_code: { in: BANKS as unknown as string[] } }, select: { id: true, bank_code: true }, }); - const map = new Map(codes.map(c => [c.bank_code, c.id])); - for (const c of wanted) { - if (!map.has(c)) throw new Error(`Bank code manquant: ${c}`); + const bcMap = new Map(bcRows.map(c => [c.bank_code, c.id])); + for (const c of BANKS) { + if (!bcMap.has(c)) throw new Error(`Bank code manquant: ${c}`); } const employees = await prisma.employees.findMany({ select: { id: true } }); @@ -49,64 +49,59 @@ async function main() { } const weekDays = currentWeekDates(); + const monday = weekDays[0]; + const friday = weekDays[4]; - // Règles: - // - (index % 5) === 0 -> mileage G503 (km) - // - (index % 5) === 1 -> remboursement G517 ($) - // Les autres: pas de dépense - // On met la dépense un des jours de la semaine (déterministe mais varié). let created = 0; - for (let ei = 0; ei < employees.length; ei++) { - const e = employees[ei]; - + for (const e of employees) { + // Choisir un timesheet (le plus ancien, ou change 'asc'→'desc' si tu préfères le plus récent) const ts = await prisma.timesheets.findFirst({ where: { employee_id: e.id }, select: { id: true }, - orderBy: { id: 'asc' }, // ajuste si tu préfères par date + orderBy: { id: 'asc' }, }); if (!ts) continue; - const dayIdx = ei % 5; // 0..4 -> répartit sur la semaine - const date = weekDays[dayIdx]; + // Si l’employé a déjà une dépense cette semaine, on n’en recrée pas (≥1 garanti) + const already = await prisma.expenses.findFirst({ + where: { + timesheet_id: ts.id, + date: { gte: monday, lte: friday }, + }, + select: { id: true }, + }); + if (already) continue; - if (ei % 5 === 0) { - // Mileage (G503) — amount = km - const km = rndInt(10, 180); // 10..180 km - await prisma.expenses.create({ - data: { - timesheet_id: ts.id, - bank_code_id: map.get('G503')!, - date, - amount: km.toString(), // on stocke le nombre de km dans amount (si tu as un champ "quantity_km", remplace ici) - attachement: null, - description: `Mileage ${km} km (emp ${e.id})`, - is_approved: Math.random() < 0.6, - supervisor_comment: Math.random() < 0.2 ? 'OK' : null, - }, - }); - created++; - } else if (ei % 5 === 1) { - // Remboursement (G517) — amount = $ - const dollars = rndAmount(2000, 25000); // 20.00$..250.00$ - await prisma.expenses.create({ - data: { - timesheet_id: ts.id, - bank_code_id: map.get('G517')!, - date, - amount: dollars, - attachement: null, - description: `Remboursement ${dollars}$ (emp ${e.id})`, - is_approved: Math.random() < 0.6, - supervisor_comment: Math.random() < 0.2 ? 'OK' : null, - }, - }); - created++; - } + // Choix aléatoire du code + jour + const randomCode = BANKS[Math.floor(Math.random() * BANKS.length)]; + const bank_code_id = bcMap.get(randomCode)!; + const date = weekDays[Math.floor(Math.random() * weekDays.length)]; + + // Montant aléatoire (ranges par défaut en $ — ajuste au besoin) + // (ex.: G57 plus petit, G517 remboursement plus large) + const amount = + randomCode === 'G57' + ? rndAmount(1000, 7500) // 10.00..75.00 + : rndAmount(2000, 25000); // 20.00..250.00 pour les autres + + await prisma.expenses.create({ + data: { + timesheet_id: ts.id, + bank_code_id, + date, + amount, // stocké en string + attachement: null, // garde le champ tel quel si typo volontaire + description: `Expense ${randomCode} ${amount}$ (emp ${e.id})`, + is_approved: Math.random() < 0.6, + supervisor_comment: Math.random() < 0.2 ? 'OK' : null, + }, + }); + created++; } const total = await prisma.expenses.count(); - console.log(`✓ Expenses: ${created} nouvelles lignes, ${total} total rows (semaine courante)`); + console.log(`✓ Expenses: ${created} nouvelles lignes, ${total} total rows (≥1 expense/employee pour la semaine courante)`); } main().finally(() => prisma.$disconnect());