fix(seeds): added larger scope of data to print

This commit is contained in:
Matthieu Haineault 2025-08-29 09:34:30 -04:00
parent 0516736fa2
commit 4bb42ec3ed
4 changed files with 127 additions and 121 deletions

View File

@ -24,15 +24,19 @@ async function main() {
const pick = <T>(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,7 +44,6 @@ async function main() {
first_name: fn,
last_name: ln,
email: emailFor(i),
// on concatène proprement en string
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());

View File

@ -32,46 +32,52 @@ function randomTitle() {
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() {
const employeeUsers = await prisma.users.findMany({
where: { role: { in: [Roles.ADMIN, Roles.SUPERVISOR, Roles.HR, Roles.ACCOUNTING, Roles.EMPLOYEE] } },
orderBy: { email: 'asc' },
});
// Create supervisors first
const supervisorUsers = employeeUsers.filter(u => u.role === Roles.SUPERVISOR);
const supervisorEmployeeIds: number[] = [];
// 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 !");
}
for (const u of supervisorUsers) {
const emp = await prisma.employees.upsert({
where: { user_id: u.id },
update: {},
// 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: u.id,
user_id: supervisorUser.id,
external_payroll_id: randInt(10000, 99999),
company_code: randInt(1, 5),
company_code: randomCompanyCode(),
first_work_day: randomPastDate(3),
last_work_day: null,
job_title: randomTitle(),
is_supervisor: true,
},
});
supervisorEmployeeIds.push(emp.id);
}
// Create remaining employees, assign a random supervisor (admin can have none)
// 3) Créer tous les autres employés avec ce superviseur (sauf ADMIN qui na 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());

View File

@ -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 dun 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<number, { id: number }[]>();
{
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 à lemployé (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,

View File

@ -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 lemployé a déjà une dépense cette semaine, on nen 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;
// 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
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')!,
bank_code_id,
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})`,
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++;
} 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++;
}
}
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());