refactor(validation): Partial modification of the file structure for validation process, added migration 20250806
This commit is contained in:
parent
b0406b3a4c
commit
cb6ec29992
|
|
@ -0,0 +1,2 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "shifts" ADD COLUMN "is_approved" BOOLEAN NOT NULL DEFAULT false;
|
||||
|
|
@ -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,7 +284,8 @@ enum LeaveTypes {
|
|||
BEREAVEMENT // deuil de famille
|
||||
PARENTAL // maternite/paternite/adoption
|
||||
LEGAL // obligations legales comme devoir de juree
|
||||
|
||||
WEDDING // mariage
|
||||
|
||||
@@map("leave_types")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
],
|
||||
|
|
|
|||
11
src/modules/expenses/services/expenses-approval.service.ts
Normal file
11
src/modules/expenses/services/expenses-approval.service.ts
Normal 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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { TimesheetsApprovalService } from "src/modules/timesheets/services/timesheets-approval.service";
|
||||
|
||||
export class PayPeriodsApprovalService {
|
||||
constructor(private readonly timesheetsApproval: TimesheetsApprovalService) {}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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,57 +8,67 @@ 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){}
|
||||
|
||||
@Post()
|
||||
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
@ApiOperation({ summary: 'Create shift' })
|
||||
@ApiResponse({ status: 201, description: 'Shift created',type: ShiftEntity })
|
||||
@ApiResponse({ status: 400, description: 'Incomplete task or invalid data' })
|
||||
create(@Body() dto: CreateShiftDto): Promise<Shifts> {
|
||||
return this.shiftsService.create(dto);
|
||||
}
|
||||
constructor(
|
||||
private readonly shiftsService: ShiftsService,
|
||||
private readonly shiftsApprovalService: ShiftsApprovalService,
|
||||
){}
|
||||
|
||||
@Get()
|
||||
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
@ApiOperation({ summary: 'Find all shifts' })
|
||||
@ApiResponse({ status: 201, description: 'List of shifts found',type: ShiftEntity, isArray: true })
|
||||
@ApiResponse({ status: 400, description: 'List of shifts not found' })
|
||||
findAll(): Promise<Shifts[]> {
|
||||
return this.shiftsService.findAll();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
@ApiOperation({ summary: 'Find shift' })
|
||||
@ApiResponse({ status: 201, description: 'Shift found',type: ShiftEntity })
|
||||
@ApiResponse({ status: 400, description: 'Shift not found' })
|
||||
findOne(@Param('id', ParseIntPipe) id: number): Promise<Shifts> {
|
||||
return this.shiftsService.findOne(id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
@ApiOperation({ summary: 'Update shift' })
|
||||
@ApiResponse({ status: 201, description: 'Shift updated',type: ShiftEntity })
|
||||
@ApiResponse({ status: 400, description: 'Shift not found' })
|
||||
update(@Param('id', ParseIntPipe) id: number,@Body() dto: UpdateShiftsDto): Promise<Shifts> {
|
||||
return this.shiftsService.update(id, dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
@ApiOperation({ summary: 'Delete shift' })
|
||||
@ApiResponse({ status: 201, description: 'Shift deleted',type: ShiftEntity })
|
||||
@ApiResponse({ status: 400, description: 'Shift not found' })
|
||||
remove(@Param('id', ParseIntPipe) id: number): Promise<Shifts> {
|
||||
return this.shiftsService.remove(id);
|
||||
}
|
||||
@Post()
|
||||
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
@ApiOperation({ summary: 'Create shift' })
|
||||
@ApiResponse({ status: 201, description: 'Shift created',type: ShiftEntity })
|
||||
@ApiResponse({ status: 400, description: 'Incomplete task or invalid data' })
|
||||
create(@Body() dto: CreateShiftDto): Promise<Shifts> {
|
||||
return this.shiftsService.create(dto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
@ApiOperation({ summary: 'Find all shifts' })
|
||||
@ApiResponse({ status: 201, description: 'List of shifts found',type: ShiftEntity, isArray: true })
|
||||
@ApiResponse({ status: 400, description: 'List of shifts not found' })
|
||||
findAll(): Promise<Shifts[]> {
|
||||
return this.shiftsService.findAll();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
@ApiOperation({ summary: 'Find shift' })
|
||||
@ApiResponse({ status: 201, description: 'Shift found',type: ShiftEntity })
|
||||
@ApiResponse({ status: 400, description: 'Shift not found' })
|
||||
findOne(@Param('id', ParseIntPipe) id: number): Promise<Shifts> {
|
||||
return this.shiftsService.findOne(id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
@ApiOperation({ summary: 'Update shift' })
|
||||
@ApiResponse({ status: 201, description: 'Shift updated',type: ShiftEntity })
|
||||
@ApiResponse({ status: 400, description: 'Shift not found' })
|
||||
update(@Param('id', ParseIntPipe) id: number,@Body() dto: UpdateShiftsDto): Promise<Shifts> {
|
||||
return this.shiftsService.update(id, dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
@ApiOperation({ summary: 'Delete shift' })
|
||||
@ApiResponse({ status: 201, description: 'Shift deleted',type: ShiftEntity })
|
||||
@ApiResponse({ status: 400, description: 'Shift not found' })
|
||||
remove(@Param('id', ParseIntPipe) id: number): Promise<Shifts> {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
21
src/modules/shifts/services/shifts-approval.service.ts
Normal file
21
src/modules/shifts/services/shifts-approval.service.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user