fix(seeds): ajusted seeds for shifts and expenses
This commit is contained in:
parent
5b2377796a
commit
9085e71f0c
|
|
@ -0,0 +1,2 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "public"."users" ALTER COLUMN "phone_number" SET DATA TYPE TEXT;
|
||||
|
|
@ -9,6 +9,7 @@ async function main() {
|
|||
['EVENING' ,'SHIFT', 1.25, 'G43'],
|
||||
['Emergency','SHIFT', 2 , 'G48'],
|
||||
['HOLIDAY' ,'SHIFT', 2.0 , 'G700'],
|
||||
|
||||
|
||||
['EXPENSES','EXPENSE', 1.0 , 'G517'],
|
||||
['MILEAGE' ,'EXPENSE', 0.72, 'G57'],
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
import { PrismaClient, Roles } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
const BASE_PHONE = '1_100_000_000'; // < 2_147_483_647
|
||||
|
||||
// base sans underscore, en string
|
||||
const BASE_PHONE = "1100000000";
|
||||
|
||||
function emailFor(i: number) {
|
||||
return `user${i + 1}@example.test`;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// 50 users total: 40 employees + 10 customers
|
||||
// Roles distribution for the 40 employees:
|
||||
// 1 ADMIN, 4 SUPERVISOR, 1 HR, 1 ACCOUNTING, 33 EMPLOYEE
|
||||
// 10 CUSTOMER (non-employees)
|
||||
const usersData: {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
|
|
@ -24,7 +22,6 @@ async function main() {
|
|||
const firstNames = ['Alex','Sam','Chris','Jordan','Taylor','Morgan','Jamie','Robin','Avery','Casey'];
|
||||
const lastNames = ['Smith','Johnson','Williams','Brown','Jones','Miller','Davis','Wilson','Taylor','Clark'];
|
||||
|
||||
// helper to pick
|
||||
const pick = <T>(arr: T[]) => arr[Math.floor(Math.random() * arr.length)];
|
||||
|
||||
const rolesForEmployees: Roles[] = [
|
||||
|
|
@ -37,14 +34,14 @@ async function main() {
|
|||
|
||||
// 40 employees
|
||||
for (let i = 0; i < 40; i++) {
|
||||
|
||||
const fn = pick(firstNames);
|
||||
const ln = pick(lastNames);
|
||||
usersData.push({
|
||||
first_name: fn,
|
||||
last_name: ln,
|
||||
email: emailFor(i),
|
||||
phone_number: BASE_PHONE + i,
|
||||
// on concatène proprement en string
|
||||
phone_number: BASE_PHONE + i.toString(),
|
||||
residence: Math.random() < 0.5 ? 'QC' : 'ON',
|
||||
role: rolesForEmployees[i],
|
||||
});
|
||||
|
|
@ -58,7 +55,7 @@ async function main() {
|
|||
first_name: fn,
|
||||
last_name: ln,
|
||||
email: emailFor(i),
|
||||
phone_number: BASE_PHONE + i,
|
||||
phone_number: BASE_PHONE + i.toString(),
|
||||
residence: Math.random() < 0.5 ? 'QC' : 'ON',
|
||||
role: Roles.CUSTOMER,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,46 +2,107 @@ import { PrismaClient } from '@prisma/client';
|
|||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
function timeAt(hour:number, minute:number) {
|
||||
// stocker une heure (Postgres TIME) via Date (UTC 1970-01-01)
|
||||
// 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));
|
||||
}
|
||||
function daysAgo(n:number) {
|
||||
const d = new Date();
|
||||
d.setUTCDate(d.getUTCDate() - n);
|
||||
d.setUTCHours(0,0,0,0);
|
||||
|
||||
// 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
|
||||
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();
|
||||
return Array.from({ length: 5 }, (_, i) => {
|
||||
const d = new Date(monday);
|
||||
d.setUTCDate(monday.getUTCDate() + i);
|
||||
return d;
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const bankCodes = await prisma.bankCodes.findMany({ where: { categorie: 'SHIFT' }, select: { id: true } });
|
||||
if (!bankCodes.length) throw new Error('Need SHIFT bank codes');
|
||||
// On récupère les bank codes requis (ajuste le nom de la colonne "code" si besoin)
|
||||
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}`);
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
for (const e of employees) {
|
||||
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.
|
||||
});
|
||||
if (!tss.length) continue;
|
||||
|
||||
// 10 shifts / employee
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const ts = tss[i % tss.length];
|
||||
const bc = bankCodes[i % bankCodes.length];
|
||||
const date = daysAgo(7 + i); // la dernière quinzaine
|
||||
const startH = 8 + (i % 3); // 8..10
|
||||
const endH = startH + 7 + (i % 2); // 15..17
|
||||
// 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
|
||||
|
||||
// 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';
|
||||
|
||||
// 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)!;
|
||||
|
||||
// Durée : 8h habituellement, 7h le vendredi pour varier (di==4)
|
||||
const duration = (di === 4) ? 7 : 8;
|
||||
|
||||
// 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;
|
||||
|
||||
const endH = startH + duration;
|
||||
const endM = startM;
|
||||
|
||||
const ts = tss[di % tss.length];
|
||||
|
||||
await prisma.shifts.create({
|
||||
data: {
|
||||
timesheet_id: ts.id,
|
||||
bank_code_id: bc.id,
|
||||
description: `Shift ${i + 1} for emp ${e.id}`,
|
||||
date,
|
||||
start_time: timeAt(startH, 0),
|
||||
end_time: timeAt(endH, 0),
|
||||
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,
|
||||
},
|
||||
});
|
||||
|
|
@ -49,7 +110,7 @@ async function main() {
|
|||
}
|
||||
|
||||
const total = await prisma.shifts.count();
|
||||
console.log(`✓ Shifts: ${total} total rows`);
|
||||
console.log(`✓ Shifts: ${total} total rows (semaine courante L→V)`);
|
||||
}
|
||||
|
||||
main().finally(() => prisma.$disconnect());
|
||||
|
|
|
|||
|
|
@ -2,43 +2,111 @@ import { PrismaClient } from '@prisma/client';
|
|||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
function daysAgo(n:number) {
|
||||
const d = new Date();
|
||||
d.setUTCDate(d.getUTCDate() - n);
|
||||
d.setUTCHours(0,0,0,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
|
||||
d.setUTCDate(d.getUTCDate() - diffToMonday);
|
||||
d.setUTCHours(0, 0, 0, 0);
|
||||
return d;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const expenseCodes = await prisma.bankCodes.findMany({ where: { categorie: 'EXPENSE' }, select: { id: true } });
|
||||
if (!expenseCodes.length) throw new Error('Need EXPENSE bank codes');
|
||||
// Retourne les 5 dates Lundi→Vendredi (UTC, à minuit)
|
||||
function currentWeekDates() {
|
||||
const monday = mondayOfThisWeekUTC();
|
||||
return Array.from({ length: 5 }, (_, i) => {
|
||||
const d = new Date(monday);
|
||||
d.setUTCDate(monday.getUTCDate() + i);
|
||||
return d;
|
||||
});
|
||||
}
|
||||
|
||||
const timesheets = await prisma.timesheets.findMany({ select: { id: true } });
|
||||
if (!timesheets.length) {
|
||||
console.warn('No timesheets found; aborting expenses seed.');
|
||||
function rndInt(min: number, max: number) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
function rndAmount(minCents: number, maxCents: number) {
|
||||
const cents = rndInt(minCents, maxCents);
|
||||
return (cents / 100).toFixed(2); // string (ex: "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[] } },
|
||||
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 employees = await prisma.employees.findMany({ select: { id: true } });
|
||||
if (!employees.length) {
|
||||
console.warn('Aucun employé — rien à insérer.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 5 expenses distribuées aléatoirement parmi les employés (via timesheets)
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const ts = timesheets[Math.floor(Math.random() * timesheets.length)];
|
||||
const bc = expenseCodes[i % expenseCodes.length];
|
||||
await prisma.expenses.create({
|
||||
data: {
|
||||
timesheet_id: ts.id,
|
||||
bank_code_id: bc.id,
|
||||
date: daysAgo(3 + i),
|
||||
amount: (50 + i * 10).toFixed(2),
|
||||
attachement: null,
|
||||
description: `Expense #${i + 1}`,
|
||||
is_approved: Math.random() < 0.5,
|
||||
supervisor_comment: Math.random() < 0.3 ? 'OK' : null,
|
||||
},
|
||||
const weekDays = currentWeekDates();
|
||||
|
||||
// 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];
|
||||
|
||||
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
|
||||
});
|
||||
if (!ts) continue;
|
||||
|
||||
const dayIdx = ei % 5; // 0..4 -> répartit sur la semaine
|
||||
const date = weekDays[dayIdx];
|
||||
|
||||
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('G57')!,
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
const total = await prisma.expenses.count();
|
||||
console.log(`✓ Expenses: ${total} total rows`);
|
||||
console.log(`✓ Expenses: ${created} nouvelles lignes, ${total} total rows (semaine courante)`);
|
||||
}
|
||||
|
||||
main().finally(() => prisma.$disconnect());
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ model Users {
|
|||
first_name String
|
||||
last_name String
|
||||
email String @unique
|
||||
phone_number Int @unique
|
||||
phone_number String @unique
|
||||
residence String?
|
||||
role Roles @default(GUEST)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user