fix(seeds): fix timesheet seeds
This commit is contained in:
parent
18c1ce38be
commit
c52de6ecb8
|
|
@ -425,44 +425,6 @@
|
|||
}
|
||||
},
|
||||
"/timesheets": {
|
||||
"post": {
|
||||
"operationId": "TimesheetsController_create",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateTimesheetDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Timesheet created",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateTimesheetDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Incomplete task or invalid data"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
],
|
||||
"summary": "Create timesheet",
|
||||
"tags": [
|
||||
"Timesheets"
|
||||
]
|
||||
},
|
||||
"get": {
|
||||
"operationId": "TimesheetsController_getPeriodByQuery",
|
||||
"parameters": [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- A unique constraint covering the columns `[employee_id,start_date]` on the table `timesheets` will be added. If there are existing duplicate values, this will fail.
|
||||
- Added the required column `start_date` to the `timesheets` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "public"."timesheets" ADD COLUMN "start_date" DATE NOT NULL;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "timesheets_employee_id_start_date_key" ON "public"."timesheets"("employee_id", "start_date");
|
||||
|
|
@ -2,26 +2,59 @@ import { PrismaClient, Prisma } from '@prisma/client';
|
|||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
// ====== Config ======
|
||||
const PREVIOUS_WEEKS = 16; // nombre de semaines à créer (passé)
|
||||
const INCLUDE_CURRENT = false; // true si tu veux aussi la semaine 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, ...
|
||||
const diffToMonday = (day + 6) % 7; // 0 si lundi
|
||||
d.setUTCDate(d.getUTCDate() - diffToMonday);
|
||||
d.setUTCHours(0, 0, 0, 0);
|
||||
return d;
|
||||
}
|
||||
function mondayNWeeksBefore(monday: Date, n: number) {
|
||||
const d = new Date(monday);
|
||||
d.setUTCDate(d.getUTCDate() - n * 7);
|
||||
return d;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const employees = await prisma.employees.findMany({ select: { id: true } });
|
||||
if (!employees.length) {
|
||||
console.warn('Aucun employé — rien à insérer.');
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ typer rows pour éviter never[]
|
||||
// Construit la liste des lundis (1 par semaine)
|
||||
const mondays: Date[] = [];
|
||||
const mondayThisWeek = mondayOfThisWeekUTC();
|
||||
if (INCLUDE_CURRENT) mondays.push(mondayThisWeek);
|
||||
for (let n = 1; n <= PREVIOUS_WEEKS; n++) {
|
||||
mondays.push(mondayNWeeksBefore(mondayThisWeek, n));
|
||||
}
|
||||
|
||||
// Prépare les lignes (1 timesheet / employé / semaine)
|
||||
const rows: Prisma.TimesheetsCreateManyInput[] = [];
|
||||
|
||||
// 8 timesheets / employee
|
||||
for (const e of employees) {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
const is_approved = Math.random() < 0.3;
|
||||
rows.push({ employee_id: e.id, is_approved });
|
||||
for (const monday of mondays) {
|
||||
rows.push({
|
||||
employee_id: e.id,
|
||||
start_date: monday,
|
||||
is_approved: Math.random() < 0.3,
|
||||
} as Prisma.TimesheetsCreateManyInput);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert en bulk et ignore les doublons si déjà présents
|
||||
if (rows.length) {
|
||||
await prisma.timesheets.createMany({ data: rows });
|
||||
await prisma.timesheets.createMany({ data: rows, skipDuplicates: true });
|
||||
}
|
||||
|
||||
const total = await prisma.timesheets.count();
|
||||
console.log(`✓ Timesheets: ${total} rows (added ${rows.length})`);
|
||||
console.log(`✓ Timesheets: ${total} rows (ajout potentiel: ${rows.length}, ${INCLUDE_CURRENT ? 'courante +' : ''}${PREVIOUS_WEEKS} semaines)`);
|
||||
}
|
||||
|
||||
main().finally(() => prisma.$disconnect());
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ const prisma = new PrismaClient();
|
|||
const PREVIOUS_WEEKS = 5;
|
||||
const INCLUDE_CURRENT = false;
|
||||
|
||||
// Times-only via Date (UTC 1970-01-01)
|
||||
function timeAt(hour: number, minute: number) {
|
||||
return new Date(Date.UTC(1970, 0, 1, hour, minute, 0));
|
||||
}
|
||||
|
||||
// Lundi (UTC) de la date fournie
|
||||
function mondayOfThisWeekUTC(now = new Date()) {
|
||||
const d = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
||||
const day = d.getUTCDay();
|
||||
|
|
@ -37,6 +39,16 @@ function rndInt(min: number, max: number) {
|
|||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
// Helper: garantit le timesheet de la semaine (upsert)
|
||||
async function getOrCreateTimesheet(employee_id: number, start_date: Date) {
|
||||
return prisma.timesheets.upsert({
|
||||
where: { employee_id_start_date: { employee_id, start_date } },
|
||||
update: {},
|
||||
create: { employee_id, start_date, is_approved: Math.random() < 0.3 },
|
||||
select: { id: true },
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// Bank codes utilisés
|
||||
const BANKS = ['G1', 'G56', 'G48', 'G700', 'G105', 'G305', 'G43'] as const;
|
||||
|
|
@ -55,21 +67,8 @@ async function main() {
|
|||
return;
|
||||
}
|
||||
|
||||
const tsByEmp = new Map<number, { id: number }[]>();
|
||||
{
|
||||
const allTs = await prisma.timesheets.findMany({
|
||||
where: { employee_id: { in: employees.map(e => e.id) } },
|
||||
select: { id: true, employee_id: true },
|
||||
orderBy: { id: 'asc' },
|
||||
});
|
||||
for (const e of employees) {
|
||||
tsByEmp.set(e.id, allTs.filter(t => t.employee_id === e.id).map(t => ({ id: t.id })));
|
||||
}
|
||||
}
|
||||
|
||||
const mondayThisWeek = mondayOfThisWeekUTC();
|
||||
const mondays: Date[] = [];
|
||||
|
||||
if (INCLUDE_CURRENT) mondays.push(mondayThisWeek);
|
||||
for (let n = 1; n <= PREVIOUS_WEEKS; n++) {
|
||||
mondays.push(mondayNWeeksBefore(mondayThisWeek, n));
|
||||
|
|
@ -83,8 +82,6 @@ async function main() {
|
|||
|
||||
for (let ei = 0; ei < employees.length; ei++) {
|
||||
const e = employees[ei];
|
||||
const tss = tsByEmp.get(e.id) ?? [];
|
||||
if (!tss.length) continue;
|
||||
|
||||
const baseStartHour = 6 + (ei % 5);
|
||||
const baseStartMinute = (ei * 15) % 60;
|
||||
|
|
@ -92,20 +89,22 @@ async function main() {
|
|||
for (let di = 0; di < weekDays.length; di++) {
|
||||
const date = weekDays[di];
|
||||
|
||||
// Tirage aléatoire du bank_code
|
||||
// 1) Trouver/Créer le timesheet de CETTE semaine pour CET employé
|
||||
const weekStart = mondayOfThisWeekUTC(date);
|
||||
const ts = await getOrCreateTimesheet(e.id, weekStart);
|
||||
|
||||
// 2) Tirage aléatoire du bank_code
|
||||
const randomCode = BANKS[Math.floor(Math.random() * BANKS.length)];
|
||||
const bank_code_id = bcMap.get(randomCode)!;
|
||||
|
||||
// 3) Horaire
|
||||
const duration = rndInt(4, 10);
|
||||
|
||||
const dayWeekOffset = (di + wi + (ei % 3)) % 3;
|
||||
const startH = Math.min(12, baseStartHour + dayWeekOffset);
|
||||
const startM = baseStartMinute;
|
||||
const endH = startH + duration;
|
||||
const endM = startM;
|
||||
|
||||
const ts = tss[(di + wi) % tss.length];
|
||||
|
||||
await prisma.shifts.create({
|
||||
data: {
|
||||
timesheet_id: ts.id,
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ import { PrismaClient } from '@prisma/client';
|
|||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
// Lundi (UTC) de la semaine courante
|
||||
// Lundi (UTC) de la date fournie
|
||||
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;
|
||||
|
|
@ -27,7 +27,17 @@ function rndInt(min: number, max: number) {
|
|||
}
|
||||
function rndAmount(minCents: number, maxCents: number) {
|
||||
const cents = rndInt(minCents, maxCents);
|
||||
return (cents / 100).toFixed(2); // string "123.45"
|
||||
return (cents / 100).toFixed(2);
|
||||
}
|
||||
|
||||
// Helper: garantit le timesheet de la semaine (upsert)
|
||||
async function getOrCreateTimesheet(employee_id: number, start_date: Date) {
|
||||
return prisma.timesheets.upsert({
|
||||
where: { employee_id_start_date: { employee_id, start_date } },
|
||||
update: {},
|
||||
create: { employee_id, start_date, is_approved: Math.random() < 0.3 },
|
||||
select: { id: true },
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
|
@ -55,43 +65,35 @@ async function main() {
|
|||
let created = 0;
|
||||
|
||||
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' },
|
||||
});
|
||||
if (!ts) continue;
|
||||
// 1) Semaine courante → assurer le timesheet de la semaine
|
||||
const weekStart = mondayOfThisWeekUTC();
|
||||
const ts = await getOrCreateTimesheet(e.id, weekStart);
|
||||
|
||||
// Si l’employé a déjà une dépense cette semaine, on n’en recrée pas (≥1 garanti)
|
||||
// 2) Skip si l’employé a déjà une dépense cette semaine (on garantit ≥1)
|
||||
const already = await prisma.expenses.findFirst({
|
||||
where: {
|
||||
timesheet_id: ts.id,
|
||||
date: { gte: monday, lte: friday },
|
||||
},
|
||||
where: { timesheet_id: ts.id, date: { gte: monday, lte: friday } },
|
||||
select: { id: true },
|
||||
});
|
||||
if (already) continue;
|
||||
|
||||
// Choix aléatoire du code + jour
|
||||
// 3) 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)
|
||||
// 4) Montant varié
|
||||
const amount =
|
||||
randomCode === 'G56'
|
||||
? rndAmount(1000, 7500) // 10.00..75.00
|
||||
: rndAmount(2000, 25000); // 20.00..250.00 pour les autres
|
||||
: rndAmount(2000, 25000); // 20.00..250.00
|
||||
|
||||
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
|
||||
amount,
|
||||
attachement: null,
|
||||
description: `Expense ${randomCode} ${amount}$ (emp ${e.id})`,
|
||||
is_approved: Math.random() < 0.6,
|
||||
supervisor_comment: Math.random() < 0.2 ? 'OK' : null,
|
||||
|
|
|
|||
|
|
@ -149,12 +149,14 @@ model Timesheets {
|
|||
id Int @id @default(autoincrement())
|
||||
employee Employees @relation("TimesheetEmployee", fields: [employee_id], references: [id])
|
||||
employee_id Int
|
||||
start_date DateTime @db.Date
|
||||
is_approved Boolean @default(false)
|
||||
|
||||
shift Shifts[] @relation("ShiftTimesheet")
|
||||
expense Expenses[] @relation("ExpensesTimesheet")
|
||||
archive TimesheetsArchive[] @relation("TimesheetsToArchive")
|
||||
|
||||
@@unique([employee_id, start_date], name: "employee_id_start_date")
|
||||
@@map("timesheets")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -233,8 +233,7 @@ export class PayPeriodsQueryService {
|
|||
const categorie = (shift.bank_code?.categorie || "REGULAR").toUpperCase();
|
||||
switch (categorie) {
|
||||
case "EVENING": record.evening_hours += hours; break;
|
||||
case "EMERGENCY":
|
||||
case "URGENT": record.emergency_hours += hours; break;
|
||||
case "EMERGENCY": record.emergency_hours += hours; break;
|
||||
case "OVERTIME": record.overtime_hours += hours; break;
|
||||
default: record.regular_hours += hours; break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,14 +20,14 @@ export class TimesheetsController {
|
|||
private readonly timesheetsCommand: TimesheetsCommandService,
|
||||
) {}
|
||||
|
||||
@Post()
|
||||
//@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
@ApiOperation({ summary: 'Create timesheet' })
|
||||
@ApiResponse({ status: 201, description: 'Timesheet created', type: CreateTimesheetDto })
|
||||
@ApiResponse({ status: 400, description: 'Incomplete task or invalid data' })
|
||||
create(@Body() dto: CreateTimesheetDto): Promise<Timesheets> {
|
||||
return this.timesheetsQuery.create(dto);
|
||||
}
|
||||
// @Post()
|
||||
// //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
// @ApiOperation({ summary: 'Create timesheet' })
|
||||
// @ApiResponse({ status: 201, description: 'Timesheet created', type: CreateTimesheetDto })
|
||||
// @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' })
|
||||
// create(@Body() dto: CreateTimesheetDto): Promise<Timesheets> {
|
||||
// return this.timesheetsQuery.create(dto);
|
||||
// }
|
||||
|
||||
@Get()
|
||||
//@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
|
|
|
|||
|
|
@ -17,16 +17,16 @@ export class TimesheetsQueryService {
|
|||
private readonly overtime: OvertimeService,
|
||||
) {}
|
||||
|
||||
async create(dto : CreateTimesheetDto): Promise<Timesheets> {
|
||||
const { employee_id, is_approved } = dto;
|
||||
return this.prisma.timesheets.create({
|
||||
data: { employee_id, is_approved: is_approved ?? false },
|
||||
include: {
|
||||
employee: { include: { user: true }
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
// async create(dto : CreateTimesheetDto): Promise<Timesheets> {
|
||||
// const { employee_id, is_approved } = dto;
|
||||
// return this.prisma.timesheets.create({
|
||||
// data: { employee_id, is_approved: is_approved ?? false },
|
||||
// include: {
|
||||
// employee: { include: { user: true }
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
|
||||
async findAll(year: number, period_no: number, email: string): Promise<TimesheetPeriodDto> {
|
||||
//finds the employee
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user