diff --git a/prisma/mock-seeds-scripts/07-leave-requests-future.ts b/prisma/mock-seeds-scripts/07-leave-requests-future.ts index b96c63d..7e07a33 100644 --- a/prisma/mock-seeds-scripts/07-leave-requests-future.ts +++ b/prisma/mock-seeds-scripts/07-leave-requests-future.ts @@ -1,82 +1,82 @@ -import { PrismaClient, Prisma, LeaveTypes, LeaveApprovalStatus } from '@prisma/client'; +// import { PrismaClient, Prisma, LeaveTypes, LeaveApprovalStatus } from '@prisma/client'; -if (process.env.SKIP_LEAVE_REQUESTS === 'true') { - console.log('?? Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)'); - process.exit(0); -} +// if (process.env.SKIP_LEAVE_REQUESTS === 'true') { +// console.log('?? Seed leave-requests ignor� (SKIP_LEAVE_REQUESTS=true)'); +// process.exit(0); +// } -const prisma = new PrismaClient(); +// const prisma = new PrismaClient(); -function dateOn(y: number, m: number, d: number) { - // stocke une date (@db.Date) à minuit UTC - return new Date(Date.UTC(y, m - 1, d, 0, 0, 0)); -} +// function dateOn(y: number, m: number, d: number) { +// // stocke une date (@db.Date) � minuit UTC +// return new Date(Date.UTC(y, m - 1, d, 0, 0, 0)); +// } -async function main() { - const year = new Date().getFullYear(); - const today = new Date(); +// async function main() { +// const year = new Date().getFullYear(); +// const today = new Date(); - const employees = await prisma.employees.findMany({ select: { id: true } }); - const bankCodes = await prisma.bankCodes.findMany({ - where: { categorie: 'LEAVE' }, - select: { id: true, type: true }, - }); +// const employees = await prisma.employees.findMany({ select: { id: true } }); +// const bankCodes = await prisma.bankCodes.findMany({ +// where: { categorie: 'LEAVE' }, +// select: { id: true, type: true }, +// }); - if (!employees.length || !bankCodes.length) { - console.warn('No employees or LEAVE bank codes; aborting'); - return; - } +// if (!employees.length || !bankCodes.length) { +// console.warn('No employees or LEAVE bank codes; aborting'); +// return; +// } - const types: LeaveTypes[] = [ - LeaveTypes.SICK, - LeaveTypes.VACATION, - LeaveTypes.UNPAID, - LeaveTypes.BEREAVEMENT, - LeaveTypes.PARENTAL, - LeaveTypes.LEGAL, - LeaveTypes.WEDDING, - ]; - const statuses: LeaveApprovalStatus[] = [ - LeaveApprovalStatus.PENDING, - LeaveApprovalStatus.APPROVED, - LeaveApprovalStatus.DENIED, - LeaveApprovalStatus.CANCELLED, - LeaveApprovalStatus.ESCALATED, - ]; +// const types: LeaveTypes[] = [ +// LeaveTypes.SICK, +// LeaveTypes.VACATION, +// LeaveTypes.UNPAID, +// LeaveTypes.BEREAVEMENT, +// LeaveTypes.PARENTAL, +// LeaveTypes.LEGAL, +// LeaveTypes.WEDDING, +// ]; +// const statuses: LeaveApprovalStatus[] = [ +// LeaveApprovalStatus.PENDING, +// LeaveApprovalStatus.APPROVED, +// LeaveApprovalStatus.DENIED, +// LeaveApprovalStatus.CANCELLED, +// LeaveApprovalStatus.ESCALATED, +// ]; - const futureMonths = [8, 9, 10, 11, 12]; // Août ? Déc. (1-based) +// const futureMonths = [8, 9, 10, 11, 12]; // Ao�t ? D�c. (1-based) - const rows: Prisma.LeaveRequestsCreateManyInput[] = []; +// const rows: Prisma.LeaveRequestsCreateManyInput[] = []; - for (let i = 0; i < 10; i++) { - const emp = employees[i % employees.length]; - const m = futureMonths[i % futureMonths.length]; - const date = dateOn(year, m, 5 + i); // 5..14 - if (date <= today) continue; // garantir « futur » +// for (let i = 0; i < 10; i++) { +// const emp = employees[i % employees.length]; +// const m = futureMonths[i % futureMonths.length]; +// const date = dateOn(year, m, 5 + i); // 5..14 +// if (date <= today) continue; // garantir � futur � - const type = types[i % types.length]; - const status = statuses[i % statuses.length]; - const bc = bankCodes[i % bankCodes.length]; - const requestedHours = 4 + (i % 5); // 4 ? 8 h - const payableHours = status === LeaveApprovalStatus.APPROVED ? Math.min(requestedHours, 8) : null; +// const type = types[i % types.length]; +// const status = statuses[i % statuses.length]; +// const bc = bankCodes[i % bankCodes.length]; +// const requestedHours = 4 + (i % 5); // 4 ? 8 h +// const payableHours = status === LeaveApprovalStatus.APPROVED ? Math.min(requestedHours, 8) : null; - rows.push({ - employee_id: emp.id, - bank_code_id: bc.id, - leave_type: type, - date, - comment: `Future leave #${i + 1} (${bc.type})`, - approval_status: status, - requested_hours: requestedHours, - payable_hours: payableHours, - }); - } +// rows.push({ +// employee_id: emp.id, +// bank_code_id: bc.id, +// leave_type: type, +// date, +// comment: `Future leave #${i + 1} (${bc.type})`, +// approval_status: status, +// requested_hours: requestedHours, +// payable_hours: payableHours, +// }); +// } - if (rows.length) { - await prisma.leaveRequests.createMany({ data: rows }); - } +// if (rows.length) { +// await prisma.leaveRequests.createMany({ data: rows }); +// } - console.log(`? LeaveRequests (future): ${rows.length} rows`); -} +// console.log(`? LeaveRequests (future): ${rows.length} rows`); +// } -main().finally(() => prisma.$disconnect()); \ No newline at end of file +// main().finally(() => prisma.$disconnect()); \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index d129b59..b2171ad 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -61,7 +61,7 @@ model LeaveRequests { bank_code_id Int comment String - date DateTime @db.Date + dates DateTime[] @db.Date payable_hours Decimal? @db.Decimal(5, 2) requested_hours Decimal? @db.Decimal(5, 2) approval_status LeaveApprovalStatus @default(PENDING) @@ -69,8 +69,8 @@ model LeaveRequests { archive LeaveRequestsArchive[] @relation("LeaveRequestToArchive") - @@unique([employee_id, leave_type, date], name: "leave_per_employee_date") - @@index([employee_id, date]) + @@unique([employee_id, leave_type, dates], name: "leave_per_employee_date") + @@index([employee_id, dates]) @@map("leave_requests") } diff --git a/src/common/mappers/bank-type-id.mapper.ts b/src/common/mappers/bank-type-id.mapper.ts index 9d6b8cc..f887abf 100644 --- a/src/common/mappers/bank-type-id.mapper.ts +++ b/src/common/mappers/bank-type-id.mapper.ts @@ -1,5 +1,5 @@ import { Injectable, NotFoundException } from "@nestjs/common"; -import { Prisma, PrismaClient } from "@prisma/client"; +import { LeaveTypes, Prisma, PrismaClient } from "@prisma/client"; import { Result } from "src/common/errors/result-error.factory"; import { PrismaService } from "src/prisma/prisma.service"; @@ -46,4 +46,6 @@ export class BankCodesResolver { return { success: true, data: bank_code.type }; } + + } \ No newline at end of file diff --git a/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts b/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts index 2094ba4..dccd627 100644 --- a/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts +++ b/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts @@ -1,20 +1,28 @@ -// import { Body, Controller, Post } from "@nestjs/common"; -// import { ApiBearerAuth, ApiTags } from "@nestjs/swagger"; -// import { UpsertLeaveRequestDto } from "../dtos/upsert-leave-request.dto"; -// import { LeaveRequestsService } from "../services/leave-request.service"; +import { Body, Controller, Delete, Patch, Post, Req } from "@nestjs/common"; +import { LeaveRequestDto } from "../dtos/leave-request.dto"; +import { LeaveRequestsService } from "src/time-and-attendance/leave-requests/services/leave-request.service"; -// @ApiTags('Leave Requests') -// @ApiBearerAuth('access-token') -// // @UseGuards() -// @Controller('leave-requests') -// export class LeaveRequestController { -// constructor(private readonly leave_service: LeaveRequestsService){} +@Controller('leave-requests') +export class LeaveRequestController { + constructor(private readonly leave_service: LeaveRequestsService) { } -// @Post('upsert') -// async upsertLeaveRequest(@Body() dto: UpsertLeaveRequestDto) { -// const { action, leave_requests } = await this.leave_service.handle(dto); -// return { action, leave_requests }; -// } + // @Post('create') + // async create(@Req() req, @Body() dto: LeaveRequestDto) { + // const email = req.user?.email; + // return await this.leave_service.create(email, dto); + // } -// } + // @Delete('delete') + // async delete(@Req() req, @Body() leave_request_id: number) { + // const email = req.user?.email; + // return await this.leave_service.delete(email, leave_request_id); + // } + + // @Patch('update') + // async update(@Req() req, @Body() request_id: number, approval_status: string) { + // const email = req.user?.email; + // return await this.leave_service.update(email, request_id, approval_status); + // } + +} diff --git a/src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto.ts b/src/time-and-attendance/leave-requests/dtos/leave-request.dto.ts similarity index 55% rename from src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto.ts rename to src/time-and-attendance/leave-requests/dtos/leave-request.dto.ts index 0f420e7..9e0c822 100644 --- a/src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto.ts +++ b/src/time-and-attendance/leave-requests/dtos/leave-request.dto.ts @@ -1,23 +1,12 @@ -import { IsEmail, IsArray, ArrayNotEmpty, ArrayUnique, IsISO8601, IsIn, IsOptional, IsString, IsNumber, Min, Max, IsEnum } from "class-validator"; +import { IsEmail, IsArray, ArrayNotEmpty, ArrayUnique, IsISO8601, IsOptional, IsString, IsNumber, Min, Max, IsEnum } from "class-validator"; import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; -import { LeaveRequestViewDto } from "./leave-request-view.dto"; import { Type } from "class-transformer"; -//sets wich function to call -export const UPSERT_ACTIONS = ['create', 'update', 'delete'] as const; -export type UpsertAction = (typeof UPSERT_ACTIONS)[number]; - //sets wich types to use export const REQUEST_TYPES = Object.values(LeaveTypes) as readonly LeaveTypes[]; export type RequestTypes = (typeof REQUEST_TYPES)[number]; -//filter requests by type and action -export interface UpsertResult { - action: UpsertAction; - leave_requests: LeaveRequestViewDto[]; -} - -export class UpsertLeaveRequestDto { +export class LeaveRequestDto { @IsEmail() email!: string; @@ -27,16 +16,11 @@ export class UpsertLeaveRequestDto { @IsISO8601({}, { each: true }) dates!: string[]; - @IsOptional() @IsEnum(LeaveTypes) type!: string; - @IsIn(UPSERT_ACTIONS) - action!: UpsertAction; - - @IsOptional() @IsString() - comment?: string; + comment!: string; @IsOptional() @Type(() => Number) @@ -45,7 +29,6 @@ export class UpsertLeaveRequestDto { @Max(24) requested_hours?: number; - @IsOptional() @IsEnum(LeaveApprovalStatus) approval_status?: LeaveApprovalStatus } \ No newline at end of file diff --git a/src/time-and-attendance/leave-requests/mappers/leave-requests.mapper.ts b/src/time-and-attendance/leave-requests/mappers/leave-requests.mapper.ts index ad772a0..8999100 100644 --- a/src/time-and-attendance/leave-requests/mappers/leave-requests.mapper.ts +++ b/src/time-and-attendance/leave-requests/mappers/leave-requests.mapper.ts @@ -1,25 +1,25 @@ import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; import { Prisma } from "@prisma/client"; -import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; +import { leave_requests_select } from "src/time-and-attendance/utils/selects.utils"; -type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>; +type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leave_requests_select}>; const toNum = (value?: Prisma.Decimal | null) => value !== null && value !== undefined ? Number(value) : undefined; -export function mapRowToView(row: LeaveRequestRow): LeaveRequestViewDto { - const iso_date = row.date?.toISOString().slice(0, 10); - if (!iso_date) throw new Error(`Leave request #${row.id} has no date set.`); +// export function mapRowToView(row: LeaveRequestRow): LeaveRequestViewDto { +// const iso_date = row.dates?.toISOString().slice(0, 10); +// if (!iso_date) throw new Error(`Leave request #${row.id} has no date set.`); - return { - id: row.id, - leave_type: row.leave_type, - date: iso_date, - payable_hours: toNum(row.payable_hours), - requested_hours: toNum(row.requested_hours), - comment: row.comment, - approval_status: row.approval_status, - email: row.employee.user.email, - employee_full_name: `${row.employee.user.first_name} ${row.employee.user.last_name}`, - }; -} \ No newline at end of file +// return { +// id: row.id, +// leave_type: row.leave_type, +// date: iso_date, +// payable_hours: toNum(row.payable_hours), +// requested_hours: toNum(row.requested_hours), +// comment: row.comment, +// approval_status: row.approval_status, +// email: row.employee.user.email, +// employee_full_name: `${row.employee.user.first_name} ${row.employee.user.last_name}`, +// }; +// } \ No newline at end of file diff --git a/src/time-and-attendance/leave-requests/services/leave-request.service.ts b/src/time-and-attendance/leave-requests/services/leave-request.service.ts index bd7a6b2..a83fb0a 100644 --- a/src/time-and-attendance/leave-requests/services/leave-request.service.ts +++ b/src/time-and-attendance/leave-requests/services/leave-request.service.ts @@ -1,241 +1,186 @@ -// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; -// import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto"; -// import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; -// import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; -// import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; -// import { roundToQuarterHour } from "src/common/utils/date-utils"; -// import { LeaveRequestsUtils } from "src/time-and-attendance/leave-requests/utils/leave-request.util"; -// import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; -// import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; -// import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; -// import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; -// import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; -// import { PrismaService } from "src/prisma/prisma.service"; -// import { mapRowToView } from "../mappers/leave-requests.mapper"; -// import { normalizeDates, toDateOnly, toISODateKey } from "src/time-and-attendance/utils/date-time.utils"; -// @Injectable() -// export class LeaveRequestsService { +import { EmployeeTimesheetResolver } from "src/common/mappers/timesheet.mapper"; +import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; +import { BankCodesResolver } from "src/common/mappers/bank-type-id.mapper"; +import { LeaveApprovalStatus, LeaveRequests, LeaveTypes, Prisma, Shifts } from "@prisma/client"; +import { PrismaService } from "src/prisma/prisma.service"; +import { Result } from "src/common/errors/result-error.factory"; +import { Injectable } from "@nestjs/common"; +import { LeaveRequestDto } from "src/time-and-attendance/leave-requests/dtos/leave-request.dto"; +import { leave_requests_select } from "src/time-and-attendance/utils/selects.utils"; +import { toDateFromString, toStringFromDate } from "src/common/utils/date-utils"; +import { NormalizedLeaveRequest } from "src/time-and-attendance/utils/type.utils"; + +@Injectable() +export class LeaveRequestsService { // constructor( -// private readonly prisma: PrismaService, -// private readonly holidayService: HolidayService, -// private readonly sickLogic: SickLeaveService, -// private readonly vacationLogic: VacationService, -// private readonly leaveUtils: LeaveRequestsUtils, -// private readonly emailResolver: EmailToIdResolver, -// private readonly typeResolver: BankCodesResolver, -// ) {} +// private readonly prisma: PrismaService, +// private readonly timesheetResolver: EmployeeTimesheetResolver, +// private readonly emailResolver: EmailToIdResolver, +// private readonly typeResolver: BankCodesResolver, +// ) { } -// // handle distribution to the right service according to the selected type and action -// async handle(dto: UpsertLeaveRequestDto): Promise { -// switch (dto.type) { -// case LeaveTypes.HOLIDAY: -// if( dto.action === 'create'){ -// // return this.holidayService.create(dto); -// } else if (dto.action === 'update') { -// return this.update(dto, LeaveTypes.HOLIDAY); -// } else if (dto.action === 'delete'){ -// return this.delete(dto, LeaveTypes.HOLIDAY); -// } -// case LeaveTypes.VACATION: -// if( dto.action === 'create'){ -// // return this.vacationService.create(dto); -// } else if (dto.action === 'update') { -// return this.update(dto, LeaveTypes.VACATION); -// } else if (dto.action === 'delete'){ -// return this.delete(dto, LeaveTypes.VACATION); -// } -// case LeaveTypes.SICK: -// if( dto.action === 'create'){ -// // return this.sickLeaveService.create(dto); -// } else if (dto.action === 'update') { -// return this.update(dto, LeaveTypes.SICK); -// } else if (dto.action === 'delete'){ -// return this.delete(dto, LeaveTypes.SICK); -// } -// default: -// throw new BadRequestException(`Unsupported leave type: ${dto.type} or action: ${dto.action}`); -// } -// } -// async delete(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise { -// const email = dto.email.trim(); -// const dates = normalizeDates(dto.dates); -// const employee_id = await this.emailResolver.findIdByEmail(email); -// if (!dates.length) throw new BadRequestException("Dates array must not be empty"); -// const rows = await this.prisma.leaveRequests.findMany({ -// where: { -// employee_id: employee_id, -// leave_type: type, -// date: { in: dates.map((d) => toDateOnly(d)) }, -// }, -// select: leaveRequestsSelect, -// }); +// async create(email: string, dto: LeaveRequestDto): Promise> { +// try { +// //verify if array is empty or not +// if (!Array.isArray(dto.dates) || dto.dates.length === 0) return { success: false, error: 'no data received' }; +// //verify if email is valid or not +// const employee = await this.emailResolver.findIdByEmail(email); +// if (!employee.success) return { success: false, error: employee.error } +// //normalized dto datas to match DB's +// const normed_request = await this.normalizeRequest(dto); +// if (!normed_request.success) return { success: false, error: normed_request.error } -// if (rows.length !== dates.length) { -// const missing = dates.filter((isoDate) => !rows.some((row) => toISODateKey(row.date) === isoDate)); -// throw new NotFoundException(`No Leave request found for: ${missing.join(", ")}`); -// } - -// for (const row of rows) { -// if (row.approval_status === LeaveApprovalStatus.APPROVED) { -// const iso = toISODateKey(row.date); -// await this.leaveUtils.removeShift(email, employee_id, iso, type); -// } -// } - -// await this.prisma.leaveRequests.deleteMany({ -// where: { id: { in: rows.map((row) => row.id) } }, -// }); - -// const deleted = rows.map((row) => ({ ...mapRowToView(row), action: "delete" as const })); -// return { action: "delete", leave_requests: deleted }; -// } - -// async update(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise { -// const email = dto.email.trim(); -// const employee_id = await this.emailResolver.findIdByEmail(email); -// const bank_code = await this.typeResolver.findIdAndModifierByType(type); -// if(!bank_code) throw new NotFoundException(`bank_code not found`); -// const modifier = Number(bank_code.modifier ?? 1); -// const dates = normalizeDates(dto.dates); -// if (!dates.length) { -// throw new BadRequestException("Dates array must not be empty"); -// } - -// const entries = await Promise.all( -// dates.map(async (iso_date) => { -// const date = toDateOnly(iso_date); -// const existing = await this.prisma.leaveRequests.findUnique({ -// where: { -// leave_per_employee_date: { -// employee_id: employee_id, -// leave_type: type, -// date, -// }, -// }, -// select: leaveRequestsSelect, -// }); -// if (!existing) throw new NotFoundException(`No Leave request found for ${iso_date}`); -// return { iso_date, date, existing }; -// }), -// ); - -// const updated: LeaveRequestViewDto[] = []; - -// if (type === LeaveTypes.SICK) { -// const firstExisting = entries[0].existing; -// const fallbackRequested = -// firstExisting.requested_hours !== null && firstExisting.requested_hours !== undefined -// ? Number(firstExisting.requested_hours) -// : 8; -// const requested_hours_per_day = dto.requested_hours ?? fallbackRequested; -// const reference_date = entries.reduce( -// (latest, entry) => (entry.date > latest ? entry.date : latest), -// entries[0].date, -// ); -// const total_payable_hours = await this.sickLogic.calculateSickLeavePay( -// employee_id, -// reference_date, -// entries.length, -// requested_hours_per_day, -// modifier, -// ); -// let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours)); -// const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier); - -// for (const { iso_date, existing } of entries) { -// const previous_status = existing.approval_status; -// const payable = Math.min(remaining_payable_hours, daily_payable_cap); -// const payable_rounded = roundToQuarterHour(Math.max(0, payable)); -// remaining_payable_hours = roundToQuarterHour( -// Math.max(0, remaining_payable_hours - payable_rounded), -// ); - -// const row = await this.prisma.leaveRequests.update({ -// where: { id: existing.id }, -// data: { -// comment: dto.comment ?? existing.comment, -// requested_hours: requested_hours_per_day, -// payable_hours: payable_rounded, -// bank_code_id: bank_code.id, -// approval_status: dto.approval_status ?? existing.approval_status, -// }, -// select: leaveRequestsSelect, -// }); - -// const was_approved = previous_status === LeaveApprovalStatus.APPROVED; -// const is_approved = row.approval_status === LeaveApprovalStatus.APPROVED; -// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0); - -// if (!was_approved && is_approved) { -// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment); -// } else if (was_approved && !is_approved) { -// await this.leaveUtils.removeShift(email, employee_id, iso_date, type); -// } else if (was_approved && is_approved) { -// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment); -// } -// updated.push({ ...mapRowToView(row), action: "update" }); -// } -// return { action: "update", leave_requests: updated }; -// } - -// for (const { iso_date, date, existing } of entries) { -// const previous_status = existing.approval_status; -// const fallbackRequested = -// existing.requested_hours !== null && existing.requested_hours !== undefined -// ? Number(existing.requested_hours) -// : 8; -// const requested_hours = dto.requested_hours ?? fallbackRequested; - -// let payable: number; -// switch (type) { -// case LeaveTypes.HOLIDAY: -// payable = await this.holidayService.calculateHolidayPay(email, date, modifier); -// break; -// case LeaveTypes.VACATION: { -// const days_requested = requested_hours / 8; -// payable = await this.vacationLogic.calculateVacationPay( -// employee_id, -// date, -// Math.max(0, days_requested), -// modifier, -// ); -// break; -// } -// default: -// payable = existing.payable_hours !== null && existing.payable_hours !== undefined -// ? Number(existing.payable_hours) -// : requested_hours; -// } - -// const row = await this.prisma.leaveRequests.update({ -// where: { id: existing.id }, +// //creates the requests +// const request_day = await this.prisma.leaveRequests.create({ // data: { -// requested_hours, -// comment: dto.comment ?? existing.comment, -// payable_hours: payable, -// bank_code_id: bank_code.id, -// approval_status: dto.approval_status ?? existing.approval_status, +// employee_id: employee.data, +// bank_code_id: normed_request.data.bank_code_id, +// comment: normed_request.data.comment, +// dates: normed_request.data.dates, +// approval_status: dto.approval_status, +// requested_hours: dto.requested_hours, +// leave_type: normed_request.data.leave_type, // }, -// select: leaveRequestsSelect, +// select: leave_requests_select, // }); +// if (!request_day) return { success: false, error: 'An error occured during creation. Leave-Request is invalid' } -// const was_approved = previous_status === LeaveApprovalStatus.APPROVED; -// const is_approved = row.approval_status === LeaveApprovalStatus.APPROVED; -// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0); +// const created_request: LeaveRequestDto = { +// email: dto.email, +// type: request_day.leave_type.toString(), +// dates: dto.dates, +// comment: normed_request.data.comment, +// approval_status: 'PENDING', +// requested_hours: dto.requested_hours, +// }; -// if (!was_approved && is_approved) { -// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment); -// } else if (was_approved && !is_approved) { -// await this.leaveUtils.removeShift(email, employee_id, iso_date, type); -// } else if (was_approved && is_approved) { -// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment); -// } -// updated.push({ ...mapRowToView(row), action: "update" }); +// return { success: true, data: created_request }; +// } catch (error) { +// return { success: false, error: `An error occured during creation, invalid data` } // } -// return { action: "update", leave_requests: updated }; // } -// } + +// async update(email: string, request_id: number, dto: LeaveRequestDto): Promise> { +// try { +// const employee = await this.emailResolver.findIdByEmail(email); +// if (!employee.success) return { success: false, error: employee.error } + +// switch (dto.approval_status) { +// case 'APPROVED': //creation of shifts and returns void +// break; +// case 'DENIED': //simple update and returns void +// break; +// case 'CANCELLED': //CANCELLED, simple update and returns void +// break; +// case 'PENDING': this.updatePendingRequest(dto) +// break; +// default: return { success: false, error: `invalid approval_status` }; +// } + +// // const updated_request: LeaveRequestDto = { +// // email: dto.email, +// // type: request_day.leave_type.toString(), +// // dates: dto.dates, +// // comment: normed_request.data.comment, +// // approval_status: dto.approval_status, +// // requested_hours: dto.requested_hours, +// // } + +// } catch (error) { +// return { success: false, error: ' An error occured during update, Invalid Leave-Request data' } +// } +// return { success: true, data: updated_request }; +// } + +// async delete(email: string, request_id: number): Promise> { +// try { +// const employee = await this.emailResolver.findIdByEmail(email); +// if (!employee.success) return { success: false, error: employee.error } + +// const deleted = await this.prisma.leaveRequests.findUnique({ +// where: { id: request_id, employee_id: employee.data }, +// select: { id: true, dates: true }, +// }); +// if (!deleted) return { success: false, error: `Leave Request with id ${request_id} not found ` }; +// return { success: true, data: deleted.id }; + +// } catch (error) { +// return { success: false, error: `INVALID_REQUEST, leave-request with id ${request_id} not found` } +// } +// } + +// private normalizeRequest = async (dto: LeaveRequestDto): Promise> => { +// const bank_code = await this.typeResolver.findBankCodeIDByType(dto.type); +// if (!bank_code.success) return { success: false, error: bank_code.error }; +// const comment = this.truncate280(dto.comment); +// //maps enum, check if dto.type is include in the list and return a valid type +// const leave_type_list = Object.values(LeaveTypes); +// const leave_type = leave_type_list.includes(dto.type.toUpperCase() as LeaveTypes); +// if (!leave_type) return { success: false, error: `Leave Request of type ${dto.type} is invalid` } +// const valid_leave_type = dto.type.toUpperCase() as LeaveTypes; + +// //map of all dates in string format +// const dates = dto.dates.map(toDateFromString); +// if (!dates) return { success: false, error: 'Bad date' } + +// return { success: true, data: { comment, dates, leave_type: valid_leave_type, bank_code_id: bank_code.data } }; +// } + +// //makes sure that a string cannot exceed 280 chars +// private truncate280 = (input: string): string => { +// return input.length > 280 ? input.slice(0, 280) : input; +// } + +// private updatePendingRequest = async (request_id: number, dto: LeaveRequestDto): Promise> => { +// const normed_dto = await this.normalizeRequest(dto); +// if (!normed_dto.success) return { success: false, error: normed_dto.error } + +// const leave_request = await this.prisma.leaveRequests.findUnique({ +// where: { id: request_id }, +// select: leave_requests_select, +// }); +// if (!leave_request) return { success: false, error: `Leave Request with id: ${request_id} not found` } + +// const update = await this.prisma.leaveRequests.update({ +// where: { id: request_id }, +// data: { +// bank_code_id: normed_dto.data.bank_code_id, +// leave_type: normed_dto.data.leave_type, +// comment: normed_dto.data.comment, +// dates: normed_dto.data.dates, +// requested_hours: dto.requested_hours ?? 0, +// } +// }) +// // (alias) class LeaveRequestDto { +// // email: string; +// // dates: string[]; +// // type: string; +// // comment: string; +// // requested_hours?: number | undefined; +// // approval_status?: $Enums.LeaveApprovalStatus | undefined; +// // } +// // const dates_string = update.dates.map(toStringFromDate); +// // const updated_request: LeaveRequestDto = { +// // email: leave_request, +// // type: update.leave_type, + +// // } + +// return { success: true, data: updated_request } +// } +} + +type leaveRequests = { + id: number; + bank_code_id: number; + comment: string; + dates: Date[]; + payable_hours: Prisma.Decimal | null; + requested_hours: Prisma.Decimal | null; + approval_status: LeaveApprovalStatus; + leave_type: LeaveTypes; +} diff --git a/src/time-and-attendance/leave-requests/utils/leave-request.transform.ts b/src/time-and-attendance/leave-requests/utils/leave-request.transform.ts index 47f76dc..b49d038 100644 --- a/src/time-and-attendance/leave-requests/utils/leave-request.transform.ts +++ b/src/time-and-attendance/leave-requests/utils/leave-request.transform.ts @@ -1,22 +1,22 @@ import { Prisma } from "@prisma/client"; import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; import { mapArchiveRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests-archive.mapper"; -import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; +// import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; import { LeaveRequestArchiveRow } from "src/time-and-attendance/leave-requests/utils/leave-requests-archive.select"; -import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; +// import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; -export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>; +// export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>; -/** Active (table leave_requests) : proxy to base mapper */ -export function mapRowToViewWithDays(row: LeaveRequestRow): LeaveRequestViewDto { - return mapRowToView(row); -} +// /** Active (table leave_requests) : proxy to base mapper */ +// export function mapRowToViewWithDays(row: LeaveRequestRow): LeaveRequestViewDto { +// return mapRowToView(row); +// } -/** Archive (table leave_requests_archive) : proxy to base mapper */ -export function mapArchiveRowToViewWithDays( - row: LeaveRequestArchiveRow, - email: string, - employee_full_name?: string, -): LeaveRequestViewDto { - return mapArchiveRowToView(row, email, employee_full_name!); -} \ No newline at end of file +// /** Archive (table leave_requests_archive) : proxy to base mapper */ +// export function mapArchiveRowToViewWithDays( +// row: LeaveRequestArchiveRow, +// email: string, +// employee_full_name?: string, +// ): LeaveRequestViewDto { +// return mapArchiveRowToView(row, email, employee_full_name!); +// } \ No newline at end of file diff --git a/src/time-and-attendance/leave-requests/utils/leave-request.util.ts b/src/time-and-attendance/leave-requests/utils/leave-request.util.ts index 5c10521..7d169d9 100644 --- a/src/time-and-attendance/leave-requests/utils/leave-request.util.ts +++ b/src/time-and-attendance/leave-requests/utils/leave-request.util.ts @@ -7,15 +7,16 @@ import { toDateFromString, toStringFromDate } from "src/common/utils/date-utils" export class LeaveRequestsUtils { constructor( private readonly prisma: PrismaService, - ){} + ) { } + async syncShift( - email: string, + email: string, employee_id: number, - date: string, - hours: number, - type: LeaveTypes, - comment?: string, + date: string, + hours: number, + type: LeaveTypes, + comment?: string, ) { if (hours <= 0) return; diff --git a/src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto.ts b/src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto.ts index d6b480f..0626bae 100644 --- a/src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto.ts +++ b/src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto.ts @@ -62,6 +62,6 @@ export class Expense { amount?: number; mileage?: number; attachment?: string; - expense_id?: number | null; + id?: number | null; supervisor_comment?: string | null; } \ No newline at end of file diff --git a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts index e6a067b..f9be0cd 100644 --- a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -159,7 +159,7 @@ export class GetTimesheetsOverviewService { date: toStringFromDate(expense.date), amount: expense.amount != null ? Number(expense.amount) : undefined, mileage: expense.mileage != null ? Number(expense.mileage) : undefined, - expense_id: expense.id ?? null, + id: expense.id ?? null, attachment: expense.attachment_record ? String(expense.attachment_record.id) : undefined, is_approved: expense.is_approved ?? false, comment: expense.comment ?? '', diff --git a/src/time-and-attendance/utils/selects.utils.ts b/src/time-and-attendance/utils/selects.utils.ts index 7938519..f81c0f4 100644 --- a/src/time-and-attendance/utils/selects.utils.ts +++ b/src/time-and-attendance/utils/selects.utils.ts @@ -28,11 +28,11 @@ export const shift_select = { comment: true, } satisfies Prisma.ShiftsSelect; -export const leaveRequestsSelect = { +export const leave_requests_select = { id: true, bank_code_id: true, leave_type: true, - date: true, + dates: true, payable_hours: true, requested_hours: true, comment: true, diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts index 307fa60..ff70305 100644 --- a/src/time-and-attendance/utils/type.utils.ts +++ b/src/time-and-attendance/utils/type.utils.ts @@ -1,4 +1,4 @@ -import { Prisma } from "@prisma/client"; +import { LeaveTypes, Prisma } from "@prisma/client"; export type Normalized = { date: Date; @@ -16,6 +16,13 @@ export type NormalizedExpense = { parsed_attachment?: number; }; +export type NormalizedLeaveRequest = { + comment: string; + dates: Date[]; + bank_code_id: number; + leave_type: LeaveTypes; +} + export type ShiftResponse = { week_day: string; sort_order: number;