From ea76435f4f850681c34d74cc26cbde40b4ce153b Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Thu, 28 Aug 2025 14:43:23 -0400 Subject: [PATCH] fix(seeder): fix seeders --- prisma/mock-seeds-scripts/09-timesheets.ts | 2 +- prisma/mock-seeds-scripts/10-shifts.ts | 148 +++++++++++------- .../mock-seeds-scripts/11-shifts-archive.ts | 2 +- .../mock-seeds-scripts/13-expenses-archive.ts | 2 +- .../timesheets/dtos/timesheet-period.dto.ts | 2 + .../services/timesheets-query.service.ts | 20 ++- .../timesheets/utils/timesheet.helpers.ts | 8 +- 7 files changed, 117 insertions(+), 67 deletions(-) diff --git a/prisma/mock-seeds-scripts/09-timesheets.ts b/prisma/mock-seeds-scripts/09-timesheets.ts index d0dc15c..1d05345 100644 --- a/prisma/mock-seeds-scripts/09-timesheets.ts +++ b/prisma/mock-seeds-scripts/09-timesheets.ts @@ -10,7 +10,7 @@ async function main() { // 8 timesheets / employee for (const e of employees) { - for (let i = 0; i < 8; i++) { + for (let i = 0; i < 16; i++) { const is_approved = Math.random() < 0.3; rows.push({ employee_id: e.id, is_approved }); } diff --git a/prisma/mock-seeds-scripts/10-shifts.ts b/prisma/mock-seeds-scripts/10-shifts.ts index d39c36c..fc6c7a4 100644 --- a/prisma/mock-seeds-scripts/10-shifts.ts +++ b/prisma/mock-seeds-scripts/10-shifts.ts @@ -2,6 +2,10 @@ 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 + // 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)); @@ -9,18 +13,16 @@ function timeAt(hour: number, minute: number) { // Lundi de la semaine (en UTC) pour la date courante function mondayOfThisWeekUTC(now = new Date()) { - // converti en UTC (sans l'heure) 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, 1 si mardi, ... 6 si dimanche + const diffToMonday = (day + 6) % 7; // 0 si lundi d.setUTCDate(d.getUTCDate() - diffToMonday); d.setUTCHours(0, 0, 0, 0); return d; } -// Retourne les 5 dates Lundi→Vendredi (UTC, à minuit) -function currentWeekDates() { - const monday = mondayOfThisWeekUTC(); +// 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); d.setUTCDate(monday.getUTCDate() + i); @@ -28,89 +30,119 @@ function currentWeekDates() { }); } +// 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() { - // On récupère les bank codes requis (ajuste le nom de la colonne "code" si besoin) + // Bank codes utilisés const BANKS = ['G1', 'G305', 'G105'] as const; const bcRows = await prisma.bankCodes.findMany({ where: { bank_code: { in: BANKS as unknown as string[] } }, select: { id: true, bank_code: true }, }); const bcMap = new Map(bcRows.map(b => [b.bank_code, b.id])); - - // Vérifications for (const c of BANKS) { 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 weekDays = currentWeekDates(); - - // Par défaut: G1 (régulier). 1 employé sur 5 : 1 jour de la semaine passe à G305 OU G105 (au hasard) - // Horaires: on décale par employé pour garantir des horaires différents. - // - start = 7, 7h30, 8, 8h30 selon l’index employé - // - durée = 8h sauf vendredi (7h) pour varier un peu - for (let ei = 0; ei < employees.length; ei++) { - const e = employees[ei]; - - const tss = await prisma.timesheets.findMany({ - where: { employee_id: e.id }, - select: { id: true }, - orderBy: { id: 'asc' }, // ajuste si tu préfères "created_at" etc. + const tsByEmp = new Map(); + { + const allTs = await prisma.timesheets.findMany({ + where: { employee_id: { in: employees.map(e => e.id) } }, + select: { id: true, employee_id: true }, + orderBy: { id: 'asc' }, }); - if (!tss.length) continue; + for (const e of employees) { + tsByEmp.set(e.id, allTs.filter(t => t.employee_id === e.id).map(t => ({ id: t.id }))); + } + } - // Horaires spécifiques à l’employé (déterministes) - const startHalfHourSlot = ei % 4; // 0..3 -> 7:00, 7:30, 8:00, 8:30 - const startHourBase = 7 + Math.floor(startHalfHourSlot / 2); // 7 ou 8 - const startMinuteBase = (startHalfHourSlot % 2) * 30; // 0 ou 30 + // Construit la liste des semaines à insérer + const mondayThisWeek = mondayOfThisWeekUTC(); + const mondays: Date[] = []; - // Doit-on donner un jour "différent" de G1 à cet employé ? - const isSpecial = (ei % 5) === 0; // 1 sur 5 - const specialDayIdx = isSpecial ? Math.floor(Math.random() * 5) : -1; - const specialCode = isSpecial ? (Math.random() < 0.5 ? 'G305' : 'G105') : 'G1'; + if (INCLUDE_CURRENT) mondays.push(mondayThisWeek); + for (let n = 1; n <= PREVIOUS_WEEKS; n++) { + mondays.push(mondayNWeeksBefore(mondayThisWeek, n)); + } - // 5 jours (lun→ven) - for (let di = 0; di < weekDays.length; di++) { - const date = weekDays[di]; + let created = 0; - // Bank code du jour - const codeToday = (di === specialDayIdx) ? specialCode : 'G1'; - const bank_code_id = bcMap.get(codeToday)!; + // Pour chaque semaine à générer + for (let wi = 0; wi < mondays.length; wi++) { + const monday = mondays[wi]; + const weekDays = weekDatesFromMonday(monday); - // Durée : 8h habituellement, 7h le vendredi pour varier (di==4) - const duration = (di === 4) ? 7 : 8; + for (let ei = 0; ei < employees.length; ei++) { + const e = employees[ei]; + const tss = tsByEmp.get(e.id) ?? []; + if (!tss.length) continue; - // Légère variation journalière (+0..2h) pour casser la monotonie, mais bornée - const dayOffset = di % 3; // 0,1,2 - const startH = Math.min(10, startHourBase + dayOffset); - const startM = startMinuteBase; + // 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 endH = startH + duration; - const endM = startM; + // 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'; - const ts = tss[di % tss.length]; + // 5 jours (lun→ven) + for (let di = 0; di < weekDays.length; di++) { + const date = weekDays[di]; - await prisma.shifts.create({ - data: { - timesheet_id: ts.id, - bank_code_id, - description: `Shift ${di + 1} (Semaine courante) emp ${e.id} — ${codeToday}`, - date, // Date du jour (UTC minuit) - start_time: timeAt(startH, startM), - end_time: timeAt(endH, endM), - is_approved: Math.random() < 0.5, - }, - }); + // Bank code du jour + const codeToday = (di === specialDayIdx) ? specialCode : 'G1'; + const bank_code_id = bcMap.get(codeToday)!; + + // 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 startM = baseStartMinute; + + const endH = startH + duration; // <= 22 en pratique + const endM = startM; + + const ts = tss[(di + wi) % tss.length]; + + await prisma.shifts.create({ + 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) + start_time: timeAt(startH, startM), + end_time: timeAt(endH, endM), + is_approved: Math.random() < 0.5, + }, + }); + created++; + } } } const total = await prisma.shifts.count(); - console.log(`✓ Shifts: ${total} total rows (semaine courante L→V)`); + console.log(`✓ Shifts: ${created} nouvelles lignes, ${total} total rows (${INCLUDE_CURRENT ? 'courante +' : ''}${PREVIOUS_WEEKS} semaines précédentes, L→V)`); } main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/11-shifts-archive.ts b/prisma/mock-seeds-scripts/11-shifts-archive.ts index fa22ddb..110031e 100644 --- a/prisma/mock-seeds-scripts/11-shifts-archive.ts +++ b/prisma/mock-seeds-scripts/11-shifts-archive.ts @@ -21,7 +21,7 @@ async function main() { if (!tss.length) continue; const createdShiftIds: number[] = []; - for (let i = 0; i < 30; i++) { + for (let i = 0; i < 8; i++) { const ts = tss[i % tss.length]; const bc = bankCodes[i % bankCodes.length]; const date = daysAgo(200 + i); // bien dans le passé diff --git a/prisma/mock-seeds-scripts/13-expenses-archive.ts b/prisma/mock-seeds-scripts/13-expenses-archive.ts index 3d87908..01ba953 100644 --- a/prisma/mock-seeds-scripts/13-expenses-archive.ts +++ b/prisma/mock-seeds-scripts/13-expenses-archive.ts @@ -17,7 +17,7 @@ async function main() { // ✅ typer pour éviter never[] const created: Expenses[] = []; - for (let i = 0; i < 20; i++) { + for (let i = 0; i < 4; i++) { const ts = timesheets[i % timesheets.length]; const bc = expenseCodes[i % expenseCodes.length]; diff --git a/src/modules/timesheets/dtos/timesheet-period.dto.ts b/src/modules/timesheets/dtos/timesheet-period.dto.ts index e383b08..cd98926 100644 --- a/src/modules/timesheets/dtos/timesheet-period.dto.ts +++ b/src/modules/timesheets/dtos/timesheet-period.dto.ts @@ -1,11 +1,13 @@ export class ShiftDto { start: string; end : string; + bank_code: string; is_approved: boolean; } export class ExpenseDto { amount: number; + bank_code: string; is_approved: boolean; } diff --git a/src/modules/timesheets/services/timesheets-query.service.ts b/src/modules/timesheets/services/timesheets-query.service.ts index cb6f67f..59af51f 100644 --- a/src/modules/timesheets/services/timesheets-query.service.ts +++ b/src/modules/timesheets/services/timesheets-query.service.ts @@ -53,7 +53,13 @@ export class TimesheetsQueryService { timesheet: { is: { employee_id: employee.id } }, date: { gte: from, lte: to }, }, - select: { date: true,start_time: true, end_time: true, is_approved: true }, + select: { + date: true, + start_time: true, + end_time: true, + is_approved: true, + bank_code: { select: { bank_code: true } }, + }, orderBy: [{date: 'asc'}, { start_time: 'asc' }], }), this.prisma.expenses.findMany({ @@ -61,8 +67,14 @@ export class TimesheetsQueryService { timesheet: { is: { employee_id: employee.id } }, date: { gte: from, lte: to }, }, - select: { date: true, amount: true, is_approved: true, bank_code: { - select: { type: true } }, + select: { + date: true, + amount: true, + is_approved: true, + bank_code: { select: { + type: true, + bank_code: true, + } }, }, orderBy: { date: 'asc' }, }), @@ -73,6 +85,7 @@ export class TimesheetsQueryService { date: shift.date, start_time: shift.start_time, end_time: shift.end_time, + bank_code: shift.bank_code?.bank_code ?? '', is_approved: shift.is_approved ?? true, })); @@ -81,6 +94,7 @@ export class TimesheetsQueryService { amount: typeof (expense.amount as any)?.toNumber() === 'function' ? (expense.amount as any).toNumber() : Number(expense.amount), type: expense.bank_code?.type ?? 'CASH', + bank_code: expense.bank_code?.bank_code ?? '', is_approved: expense.is_approved ?? true, })); diff --git a/src/modules/timesheets/utils/timesheet.helpers.ts b/src/modules/timesheets/utils/timesheet.helpers.ts index 7e32901..29e7a1a 100644 --- a/src/modules/timesheets/utils/timesheet.helpers.ts +++ b/src/modules/timesheets/utils/timesheet.helpers.ts @@ -5,8 +5,8 @@ export const DAY_KEYS = ['sun','mon','tue','wed','thu','fri','sat'] as const; export type DayKey = typeof DAY_KEYS[number]; //DB line types -export type ShiftRow = { date: Date; start_time: Date; end_time: Date; is_approved?: boolean }; -export type ExpenseRow = { date: Date; amount: number; type: string; is_approved?: boolean }; +export type ShiftRow = { date: Date; start_time: Date; end_time: Date; bank_code: string; is_approved?: boolean }; +export type ExpenseRow = { date: Date; amount: number; type: string; bank_code: string; is_approved?: boolean }; export function dayKeyFromDate(date: Date, useUTC = true): DayKey { const index = useUTC ? date.getUTCDay() : date.getDay(); // 0=Sunday..6=Saturday @@ -119,8 +119,9 @@ export function buildWeek( week.shifts[key].shifts.push({ start: toTimeString(shift.start_time), end : toTimeString(shift.end_time), + bank_code: shift.bank_code, is_approved: shift.is_approved ?? true, - } as ShiftDto); + }); all_approved = all_approved && (shift.is_approved ?? true); } @@ -133,6 +134,7 @@ export function buildWeek( week.expenses[key][bucket].push({ amount: round2(expense.amount), is_approved: expense.is_approved ?? true, + bank_code: expense.bank_code, }); all_approved = all_approved && (expense.is_approved ?? true); }