refactor(validation): Partial modification of the file structure for validation process, added migration 20250806

This commit is contained in:
Matthieu Haineault 2025-08-06 13:15:34 -04:00
parent b0406b3a4c
commit cb6ec29992
13 changed files with 135 additions and 79 deletions

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "shifts" ADD COLUMN "is_approved" BOOLEAN NOT NULL DEFAULT false;

View File

@ -175,6 +175,7 @@ model Shifts {
date DateTime @db.Date
start_time DateTime @db.Time(0)
end_time DateTime @db.Time(0)
is_approved Boolean @default(false)
archive ShiftsArchive[] @relation("ShiftsToArchive")
@ -283,6 +284,7 @@ enum LeaveTypes {
BEREAVEMENT // deuil de famille
PARENTAL // maternite/paternite/adoption
LEGAL // obligations legales comme devoir de juree
WEDDING // mariage
@@map("leave_types")
}

View File

@ -18,7 +18,6 @@ import { ArchivalModule } from './modules/archival/archival.module';
import { BankCodesModule } from './modules/bank-codes/bank-codes.module';
import { OvertimeService } from './modules/business-logics/services/overtime.service';
import { BusinessLogicsModule } from './modules/business-logics/business-logics.module';
import { ShiftsValidationModule } from './modules/shifts/validation/shifts-validation.module';
import { OauthSessionsModule } from './modules/oauth-sessions/oauth-sessions.module';
@Module({
@ -37,7 +36,6 @@ import { OauthSessionsModule } from './modules/oauth-sessions/oauth-sessions.mod
PayperiodsModule,
PrismaModule,
ShiftsModule,
ShiftsValidationModule,
TimesheetsModule,
UsersModule,
],

View File

@ -0,0 +1,11 @@
import { Injectable } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service";
@Injectable()
export class ExpensesApprovalService {
constructor(private readonly prisma: PrismaService) {}
async updateApproval(expenseId: number, isApproved: boolean) {
}
}

View File

@ -0,0 +1,5 @@
import { TimesheetsApprovalService } from "src/modules/timesheets/services/timesheets-approval.service";
export class PayPeriodsApprovalService {
constructor(private readonly timesheetsApproval: TimesheetsApprovalService) {}
}

View File

@ -1,20 +1,20 @@
import { Controller, Get, Header, Query } from "@nestjs/common";
import { ShiftsValidationService, ValidationRow } from "../services/shifts-validation.service";
import { GetShiftsValidationDto } from "../dtos/get-shifts-validation.dto";
import { OverviewRow, ShiftsOverviewService } from "../services/shifts-overview.service";
import { GetShiftsOverviewDto } from "../dtos/get-shifts-overview.dto";
@Controller()
export class ShiftsValidationController {
constructor(private readonly shiftsValidationService: ShiftsValidationService) {}
export class ShiftsOverviewController {
constructor(private readonly shiftsValidationService: ShiftsOverviewService) {}
@Get()
async getSummary( @Query() query: GetShiftsValidationDto): Promise<ValidationRow[]> {
async getSummary( @Query() query: GetShiftsOverviewDto): Promise<OverviewRow[]> {
return this.shiftsValidationService.getSummary(query.periodId);
}
@Get('export.csv')
@Header('Content-Type', 'text/csv; charset=utf-8')
@Header('Content-Disposition', 'attachment; filename="shifts-validation.csv"')
async exportCsv(@Query() query: GetShiftsValidationDto): Promise<Buffer>{
async exportCsv(@Query() query: GetShiftsOverviewDto): Promise<Buffer>{
const rows = await this.shiftsValidationService.getSummary(query.periodId);
//CSV Headers

View File

@ -1,4 +1,4 @@
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common";
import { Body, Controller, Delete, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common";
import { ShiftsService } from "../services/shifts.service";
import { Shifts } from "@prisma/client";
import { CreateShiftDto } from "../dtos/create-shifts.dto";
@ -8,13 +8,17 @@ import { Roles as RoleEnum } from '.prisma/client';
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
import { JwtAuthGuard } from "src/modules/authentication/guards/jwt-auth.guard";
import { ShiftEntity } from "../dtos/swagger-entities/shift.entity";
import { ShiftsApprovalService } from "../services/shifts-approval.service";
@ApiTags('Shifts')
@ApiBearerAuth('access-token')
@UseGuards(JwtAuthGuard)
@Controller('shifts')
export class ShiftsController {
constructor(private readonly shiftsService: ShiftsService){}
constructor(
private readonly shiftsService: ShiftsService,
private readonly shiftsApprovalService: ShiftsApprovalService,
){}
@Post()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
@ -61,4 +65,10 @@ export class ShiftsController {
return this.shiftsService.remove(id);
}
@Patch(':id/approval')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
async approve(@Param('id', ParseIntPipe) id: number, @Body('is_approved', ParseBoolPipe) isApproved: boolean) {
return this.shiftsApprovalService.updateApproval(id, isApproved);
}
}

View File

@ -1,7 +1,7 @@
import { Type } from "class-transformer";
import { IsInt, Min, Max } from "class-validator";
export class GetShiftsValidationDto {
export class GetShiftsOverviewDto {
@Type(()=> Number)
@IsInt()
@Min(1)

View File

@ -0,0 +1,21 @@
import { Injectable, NotFoundException } from "@nestjs/common";
import { Shifts } from "@prisma/client";
import { PrismaService } from "src/prisma/prisma.service";
@Injectable()
export class ShiftsApprovalService {
constructor(private readonly prisma: PrismaService) {}
async updateApproval(shiftId: number, isApproved: boolean): Promise<Shifts> {
const shift = await this.prisma.shifts.update({
where: { id: shiftId },
data: { is_approved: isApproved },
});
if(!shift) {
throw new NotFoundException(`Shift # ${shiftId} not found`);
}
return shift;
}
}

View File

@ -1,7 +1,7 @@
import { Injectable, NotFoundException } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service";
export interface ValidationRow {
export interface OverviewRow {
fullName: string;
supervisor: string;
totalRegularHrs: number;
@ -13,7 +13,7 @@ export interface ValidationRow {
}
@Injectable()
export class ShiftsValidationService {
export class ShiftsOverviewService {
constructor(private readonly prisma: PrismaService) {}
private computeHours(start: Date, end: Date): number {
@ -22,7 +22,7 @@ export class ShiftsValidationService {
return parseFloat(hours.toFixed(2));
}
async getSummary(periodId: number): Promise<ValidationRow[]> {
async getSummary(periodId: number): Promise<OverviewRow[]> {
//fetch pay-period to display
const period = await this.prisma.payPeriods.findUnique({
where: { period_number: periodId },
@ -57,7 +57,7 @@ export class ShiftsValidationService {
},
});
const mapRow = new Map<string, ValidationRow>();
const mapRow = new Map<string, OverviewRow>();
for(const s of shifts) {
const employeeId = s.timesheet.employee.user_id;
@ -119,4 +119,5 @@ export class ShiftsValidationService {
//return by default the list of employee in ascending alphabetical order
return Array.from(mapRow.values()).sort((a,b) => a.fullName.localeCompare(b.fullName));
}
}

View File

@ -2,15 +2,13 @@ import { Module } from '@nestjs/common';
import { ShiftsController } from './controllers/shifts.controller';
import { ShiftsService } from './services/shifts.service';
import { BusinessLogicsModule } from 'src/modules/business-logics/business-logics.module';
import { ShiftsValidationModule } from './validation/shifts-validation.module';
import { ShiftsOverviewController } from './controllers/shifts-overview.controller';
import { ShiftsOverviewService } from './services/shifts-overview.service';
@Module({
imports: [
BusinessLogicsModule,
ShiftsValidationModule,
],
controllers: [ShiftsController],
providers: [ShiftsService],
exports: [ShiftsService],
imports: [BusinessLogicsModule],
controllers: [ShiftsController, ShiftsOverviewController],
providers: [ShiftsService, ShiftsOverviewService],
exports: [ShiftsService, ShiftsOverviewService],
})
export class ShiftsModule {}

View File

@ -1,11 +0,0 @@
import { Module } from "@nestjs/common";
import { ShiftsValidationController } from "./controllers/shifts-validation.controller";
import { ShiftsValidationService } from "./services/shifts-validation.service";
import { BusinessLogicsModule } from "src/modules/business-logics/business-logics.module";
@Module({
imports: [BusinessLogicsModule],
controllers: [ShiftsValidationController],
providers: [ShiftsValidationService],
})
export class ShiftsValidationModule {}

View File

@ -0,0 +1,19 @@
import { Injectable, NotFoundException } from "@nestjs/common";
import { Timesheets } from "@prisma/client";
import { PrismaService } from "src/prisma/prisma.service";
@Injectable()
export class TimesheetsApprovalService {
constructor(private readonly prisma: PrismaService) {}
async updateApproval(timesheetId: number, isApproved: boolean): Promise<Timesheets> {
const timesheet = await this.prisma.timesheets.update({
where: { id: timesheetId },
data: { is_approved: isApproved},
});
if (!timesheet) throw new NotFoundException(`Timesheet # ${timesheetId} not found`);
return timesheet;
}
}