Merge branch 'main' of git.targo.ca:Targo/targo_backend into origin/dev/setup/AuthMiddleware/NicolasD

This commit is contained in:
Nicolas Drolet 2025-07-22 16:45:40 -04:00
commit 615f0848e5
29 changed files with 3021 additions and 76 deletions

2
.gitignore vendored
View File

@ -56,3 +56,5 @@ pids
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
/generated/prisma /generated/prisma
!swagger-spec.json

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@ import { LeaveRequestsModule } from './modules/leave-requests/leave-requests.mod
import { ShiftCodeModule } from './modules/shift-codes/shift-codes.module'; import { ShiftCodeModule } from './modules/shift-codes/shift-codes.module';
import { ShiftsModule } from './modules/shifts/shifts.module'; import { ShiftsModule } from './modules/shifts/shifts.module';
import { TimesheetsModule } from './modules/timesheets/timesheets.module'; import { TimesheetsModule } from './modules/timesheets/timesheets.module';
import { AuthenticationModule } from './modules/authentication/auth.module';
@Module({ @Module({
imports: [ imports: [
@ -25,6 +26,7 @@ import { TimesheetsModule } from './modules/timesheets/timesheets.module';
ShiftCodeModule, ShiftCodeModule,
ShiftsModule, ShiftsModule,
TimesheetsModule, TimesheetsModule,
AuthenticationModule,
], ],
controllers: [AppController, HealthController], controllers: [AppController, HealthController],
providers: [AppService], providers: [AppService],

View File

@ -5,11 +5,13 @@ import { ValidationPipe } from '@nestjs/common';
import { JwtAuthGuard } from './modules/authentication/guards/jwt-auth.guard'; import { JwtAuthGuard } from './modules/authentication/guards/jwt-auth.guard';
import { RolesGuard } from './common/guards/roles.guard'; import { RolesGuard } from './common/guards/roles.guard';
import { OwnershipGuard } from './common/guards/ownership.guard'; import { OwnershipGuard } from './common/guards/ownership.guard';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { writeFileSync } from 'fs';
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create(AppModule); const app = await NestFactory.create(AppModule);
//setup Reflector for Roles()
const reflector = app.get(Reflector); const reflector = app.get(Reflector); //setup Reflector for Roles()
app.useGlobalPipes( app.useGlobalPipes(
new ValidationPipe({ whitelist: true, transform: true})); new ValidationPipe({ whitelist: true, transform: true}));
@ -19,6 +21,37 @@ async function bootstrap() {
new OwnershipGuard(reflector, app.get(ModuleRef)), //Global use of OwnershipGuard, not implemented yet new OwnershipGuard(reflector, app.get(ModuleRef)), //Global use of OwnershipGuard, not implemented yet
); );
//swagger config
const config = new DocumentBuilder()
.setTitle('Targo_Backend')
.setDescription('Documentation de l`API REST pour Targo (NestJS + Prisma)')
.setVersion('1.0')
.addBearerAuth({
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
name: 'Authorization',
description: 'Invalid JWT token',
in: 'header',
}, 'access-token')
.addTag('Users')
.addTag('Employees')
.addTag('Customers')
.addTag('Timesheets')
.addTag('Shifts')
.addTag('Leave Requests')
.addTag('Shift Codes')
.addTag('OAuth Access Tokens')
.addTag('Authorization')
.build();
//document builder for swagger docs
const documentFactory = () => SwaggerModule.createDocument(app, config);
const document = documentFactory()
SwaggerModule.setup('api/docs', app, document);
writeFileSync('./docs/swagger/swagger-spec.json', JSON.stringify(document, null, 2));
await app.listen(process.env.PORT ?? 3000); await app.listen(process.env.PORT ?? 3000);
} }
bootstrap(); bootstrap();

View File

@ -1,35 +1,53 @@
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from '@nestjs/common';
import { CustomersService } from '../services/customers.service'; import { CustomersService } from '../services/customers.service';
import { Customers, Employees } from '@prisma/client'; import { Customers } from '@prisma/client';
import { CreateCustomerDto } from '../dtos/create-customer'; import { CreateCustomerDto } from '../dtos/create-customer.dto';
import { UpdateCustomerDto } from '../dtos/update-customer'; import { UpdateCustomerDto } from '../dtos/update-customer.dto';
import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { Roles as RoleEnum } from '.prisma/client'; 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 { CustomerEntity } from '../dtos/swagger-entities/customers.entity';
@ApiTags('Customers')
@ApiBearerAuth('access-token')
@UseGuards(JwtAuthGuard)
@Controller('customers') @Controller('customers')
export class CustomersController { export class CustomersController {
constructor(private readonly customersService: CustomersService) {} constructor(private readonly customersService: CustomersService) {}
@Post() @Post()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.SUPERVISOR)
@ApiOperation({ summary: 'Create customer' })
@ApiResponse({ status: 201, description: 'Customer created', type: CustomerEntity })
@ApiResponse({ status: 400, description: 'Invalid task or invalid data' })
create(@Body() dto: CreateCustomerDto): Promise<Customers> { create(@Body() dto: CreateCustomerDto): Promise<Customers> {
return this.customersService.create(dto); return this.customersService.create(dto);
} }
@Get() @Get()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
@ApiOperation({ summary: 'Find all customers' })
@ApiResponse({ status: 201, description: 'List of customers found', type: CustomerEntity, isArray: true })
@ApiResponse({ status: 400, description: 'List of customers not found' })
findAll(): Promise<Customers[]> { findAll(): Promise<Customers[]> {
return this.customersService.findAll(); return this.customersService.findAll();
} }
@Get(':id') @Get(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
@ApiOperation({ summary: 'Find customer' })
@ApiResponse({ status: 201, description: 'Customer found', type: CustomerEntity })
@ApiResponse({ status: 400, description: 'Customer not found' })
findOne(@Param('id', ParseIntPipe) id: number): Promise<Customers> { findOne(@Param('id', ParseIntPipe) id: number): Promise<Customers> {
return this.customersService.findOne(id); return this.customersService.findOne(id);
} }
@Patch(':id') @Patch(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE,RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE,RoleEnum.SUPERVISOR)
@ApiOperation({ summary: 'Update customer' })
@ApiResponse({ status: 201, description: 'Customer updated', type: CustomerEntity })
@ApiResponse({ status: 400, description: 'Customer not found' })
update( update(
@Param('id', ParseIntPipe) id: number, @Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateCustomerDto, @Body() dto: UpdateCustomerDto,
@ -39,6 +57,9 @@ export class CustomersController {
@Delete(':id') @Delete(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.SUPERVISOR)
@ApiOperation({ summary: 'Delete customer' })
@ApiResponse({ status: 201, description: 'Customer deleted', type: CustomerEntity })
@ApiResponse({ status: 400, description: 'Customer not found' })
remove(@Param('id', ParseIntPipe) id: number): Promise<Customers>{ remove(@Param('id', ParseIntPipe) id: number): Promise<Customers>{
return this.customersService.remove(id); return this.customersService.remove(id);
} }

View File

@ -0,0 +1,64 @@
import { ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer";
import {
IsEmail,
IsInt,
IsNotEmpty,
IsOptional,
IsPositive,
IsString,
} from "class-validator";
export class CreateCustomerDto {
@ApiProperty({
example: 'Gandalf',
description: 'Customer`s first name',
})
@IsString()
@IsNotEmpty()
first_name: string;
@ApiProperty({
example: 'TheGray',
description: 'Customer`s last name',
})
@IsString()
@IsNotEmpty()
last_name: string;
@ApiProperty({
example: 'you_shall_not_pass@middleEarth.com',
description: 'Customer`s email',
})
@IsEmail()
@IsOptional()
email: string;
@ApiProperty({
example: '8436637464',
description: 'Customer`s phone number',
})
@Type(() => Number)
@IsInt()
@IsPositive()
phone_number: number;
@ApiProperty({
example: '1 Ringbearer`s way, Mount Doom city, ME, T1R 1N6 ',
description: 'Customer`s residence',
required: false,
})
@IsString()
@IsOptional()
residence?: string;
@ApiProperty({
example: '4263253',
description: 'Customer`s invoice number',
required: false,
})
@IsInt()
@IsNotEmpty()
invoice_id: number;
}

View File

@ -1,37 +0,0 @@
import { Type } from "class-transformer";
import {
IsEmail,
IsInt,
IsNotEmpty,
IsOptional,
IsPositive,
IsString,
} from "class-validator";
export class CreateCustomerDto {
@IsString()
@IsNotEmpty()
first_name: string;
@IsString()
@IsNotEmpty()
last_name: string;
@IsEmail()
@IsOptional()
email?: string;
@Type(() => Number)
@IsInt()
@IsPositive()
phone_number: number;
@IsString()
@IsOptional()
residence?: string;
@IsInt()
@IsNotEmpty()
invoice_id: number;
}

View File

@ -0,0 +1,42 @@
import { ApiProperty } from '@nestjs/swagger';
export class CustomerEntity {
@ApiProperty({
example: 1,
description: 'Unique ID of a customer(primary-key, auto-incremented)',
})
id: number;
@ApiProperty({
example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d',
description: 'UUID of the user linked to that customer',
})
user_id: string;
@ApiProperty({
example: 'you_shall_not_pass@middleEarth.com',
description: 'customer`s email (optional)',
required: false,
})
email?: string;
@ApiProperty({
example: 8436637464,
description: 'customer`s phone number (numbers only)',
})
phone_number: number;
@ApiProperty({
example: '1 Ringbearers way, Mount Doom city, ME, T1R 1N6',
description: 'customer`s residence address (optional)',
required: false,
})
residence?: string;
@ApiProperty({
example: 4263253,
description: 'customer`s invoice number (optionnal, unique)',
required: false,
})
invoice_id?: number;
}

View File

@ -1,4 +1,4 @@
import { PartialType } from "@nestjs/swagger"; import { PartialType } from "@nestjs/swagger";
import { CreateCustomerDto } from "./create-customer"; import { CreateCustomerDto } from "./create-customer.dto";
export class UpdateCustomerDto extends PartialType(CreateCustomerDto) {} export class UpdateCustomerDto extends PartialType(CreateCustomerDto) {}

View File

@ -1,8 +1,8 @@
import { Injectable, NotFoundException } from '@nestjs/common'; import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
import { CreateCustomerDto } from '../dtos/create-customer'; import { CreateCustomerDto } from '../dtos/create-customer.dto';
import { Customers, Users } from '@prisma/client'; import { Customers, Users } from '@prisma/client';
import { UpdateCustomerDto } from '../dtos/update-customer'; import { UpdateCustomerDto } from '../dtos/update-customer.dto';
@Injectable() @Injectable()
export class CustomersService { export class CustomersService {

View File

@ -1,46 +1,52 @@
import { import { Body,Controller,Delete,Get,Param,ParseIntPipe,Patch,Post,UseGuards } from '@nestjs/common';
Body, import { Employees, Roles as RoleEnum } from '@prisma/client';
Controller,
Delete,
Get,
Param,
ParseIntPipe,
Patch,
Post,
} from '@nestjs/common';
import { Employees } from '@prisma/client';
import { EmployeesService } from '../services/employees.service'; import { EmployeesService } from '../services/employees.service';
import { CreateEmployeeDto } from '../dtos/create-employee.dto'; import { CreateEmployeeDto } from '../dtos/create-employee.dto';
import { UpdateEmployeeDto } from '../dtos/update-employee.dto'; import { UpdateEmployeeDto } from '../dtos/update-employee.dto';
//decorators and roles imports
import { RolesAllowed } from '../../../common/decorators/roles.decorators'; import { RolesAllowed } from '../../../common/decorators/roles.decorators';
import { Roles as RoleEnum } from 'prisma/prisma-client'; import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { JwtAuthGuard } from 'src/modules/authentication/guards/jwt-auth.guard';
import { EmployeeEntity } from '../dtos/swagger-entities/employees.entity';
@ApiTags('Employees')
@ApiBearerAuth('access-token')
@UseGuards(JwtAuthGuard)
@Controller('employees') @Controller('employees')
export class EmployeesController { export class EmployeesController {
constructor(private readonly employeesService: EmployeesService) {} constructor(private readonly employeesService: EmployeesService) {}
@Post() @Post()
@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
@ApiOperation({summary: 'Create employee' })
@ApiResponse({ status: 201, description: 'Employee created', type: EmployeeEntity })
@ApiResponse({ status: 400, description: 'Incomplete task or invalid data' })
create(@Body() dto: CreateEmployeeDto): Promise<Employees> { create(@Body() dto: CreateEmployeeDto): Promise<Employees> {
return this.employeesService.create(dto); return this.employeesService.create(dto);
} }
@Get() @Get()
@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ACCOUNTING) @RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ACCOUNTING)
@ApiOperation({summary: 'Find all employees' })
@ApiResponse({ status: 201, description: 'List of employees found', type: EmployeeEntity, isArray: true })
@ApiResponse({ status: 400, description: 'List of employees not found' })
findAll(): Promise<Employees[]> { findAll(): Promise<Employees[]> {
return this.employeesService.findAll(); return this.employeesService.findAll();
} }
@Get(':id') @Get(':id')
@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR,RoleEnum.ACCOUNTING ) @RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR,RoleEnum.ACCOUNTING )
@ApiOperation({summary: 'Find employee' })
@ApiResponse({ status: 201, description: 'Employee found', type: EmployeeEntity })
@ApiResponse({ status: 400, description: 'Employee not found' })
findOne(@Param('id', ParseIntPipe) id: number): Promise<Employees> { findOne(@Param('id', ParseIntPipe) id: number): Promise<Employees> {
return this.employeesService.findOne(id); return this.employeesService.findOne(id);
} }
@Patch(':id') @Patch(':id')
@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR,RoleEnum.ACCOUNTING ) @RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR,RoleEnum.ACCOUNTING )
@ApiOperation({summary: 'Update employee' })
@ApiResponse({ status: 201, description: 'Employee updated', type: EmployeeEntity })
@ApiResponse({ status: 400, description: 'Employee not found' })
update( update(
@Param('id', ParseIntPipe) id: number, @Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateEmployeeDto, @Body() dto: UpdateEmployeeDto,
@ -50,6 +56,9 @@ export class EmployeesController {
@Delete(':id') @Delete(':id')
@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR ) @RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR )
@ApiOperation({summary: 'Delete employee' })
@ApiResponse({ status: 201, description: 'Employee deleted', type: EmployeeEntity })
@ApiResponse({ status: 400, description: 'Employee not found' })
remove(@Param('id', ParseIntPipe) id: number): Promise<Employees> { remove(@Param('id', ParseIntPipe) id: number): Promise<Employees> {
return this.employeesService.remove(id); return this.employeesService.remove(id);
} }

View File

@ -9,44 +9,84 @@ import {
IsString, IsString,
} from 'class-validator'; } from 'class-validator';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
import { ApiProperty } from '@nestjs/swagger';
export class CreateEmployeeDto { export class CreateEmployeeDto {
@ApiProperty({
example: 'Frodo',
description: 'Employee`s first name',
})
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
first_name: string; first_name: string;
@ApiProperty({
example: 'Baggins',
description: 'Employee`s last name',
})
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
last_name: string; last_name: string;
@ApiProperty({
example: 'i_cant_do_this_sam@targointernet.com',
description: 'Employee`s email',
})
@IsEmail() @IsEmail()
@IsOptional() @IsOptional()
email: string; email: string;
@ApiProperty({
example: '82538437464',
description: 'Employee`s phone number',
})
@Type(() => Number) @Type(() => Number)
@IsInt() @IsInt()
@IsPositive() @IsPositive()
phone_number: number; phone_number: number;
@ApiProperty({
example: '1 Bagshot Row, Hobbiton, The Shire, Middle-earth',
description: 'Employee`s residence',
required: false,
})
@IsString() @IsString()
@IsOptional() @IsOptional()
residence?: string; residence?: string;
@ApiProperty({
example: 'BagginsF7464',
description: 'Employee`s payroll id',
})
@IsInt() @IsInt()
@IsPositive() @IsPositive()
@Type(() => Number) @Type(() => Number)
external_payroll_id: number; external_payroll_id: number;
@ApiProperty({
example: '335567447',
description: 'Employee`s company code',
})
@IsInt() @IsInt()
@IsPositive() @IsPositive()
@Type(() => Number) @Type(() => Number)
company_code: number; company_code: number;
@ApiProperty({
example: '23/09/3018',
description: 'Employee`s first working day',
})
@IsDateString() @IsDateString()
@Type(() => Date) @Type(() => Date)
@IsDate() @IsDate()
first_work_day: Date; first_work_day: Date;
@ApiProperty({
example: '25/03/3019',
description: 'Employee`s last working day',
required: false,
})
@IsDateString() @IsDateString()
@Type(() => Date) @Type(() => Date)
@IsDate() @IsDate()

View File

@ -0,0 +1,40 @@
import { ApiProperty } from '@nestjs/swagger';
export class EmployeeEntity {
@ApiProperty({
example: 1,
description: 'Unique ID of an employee(primary-key, auto-incremented)',
})
id: number;
@ApiProperty({
example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d',
description: 'UUID of the user linked to that employee',
})
user_id: string;
@ApiProperty({
example: 7464,
description: 'external ID for the pay system',
})
external_payroll_id: number;
@ApiProperty({
example: 335567447,
description: 'company code',
})
company_code: number;
@ApiProperty({
example: '3018-09-23T00:00:00.000Z',
description: 'Employee first day at work',
})
first_work_day: Date;
@ApiProperty({
example: '3019-03-25T00:00:00.000Z',
description: 'Employee last day at work',
required: false,
})
last_work_day?: Date;
}

View File

@ -1,44 +1,62 @@
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common";
import { LeaveRequestsService } from "../services/leave-request.service"; import { LeaveRequestsService } from "../services/leave-request.service";
import { CreateLeaveRequestsDto } from "../dtos/create-leave-requests.dto"; import { CreateLeaveRequestsDto } from "../dtos/create-leave-requests.dto";
import { LeaveRequests } from "@prisma/client"; import { LeaveRequests } from "@prisma/client";
import { UpdateLeaveRequestsDto } from "../dtos/update-leave-requests.dto"; import { UpdateLeaveRequestsDto } from "../dtos/update-leave-requests.dto";
import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { Roles as RoleEnum } from '.prisma/client'; 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 { LeaveRequestEntity } from "../dtos/swagger-entities/leave-requests.entity";
@ApiTags('Leave Requests')
@ApiBearerAuth('access-token')
@UseGuards(JwtAuthGuard)
@Controller('leave-requests') @Controller('leave-requests')
export class LeaveRequestController { export class LeaveRequestController {
constructor(private readonly leaveRequetsService: LeaveRequestsService){} constructor(private readonly leaveRequetsService: LeaveRequestsService){}
@Post() @Post()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
@ApiOperation({summary: 'Create leave request' })
@ApiResponse({ status: 201, description: 'Leave request created',type: LeaveRequestEntity })
@ApiResponse({ status: 400, description: 'Incomplete task or invalid data' })
create(@Body() dto: CreateLeaveRequestsDto): Promise<LeaveRequests> { create(@Body() dto: CreateLeaveRequestsDto): Promise<LeaveRequests> {
return this. leaveRequetsService.create(dto); return this. leaveRequetsService.create(dto);
} }
@Get() @Get()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
@ApiOperation({summary: 'Find all leave request' })
@ApiResponse({ status: 201, description: 'List of Leave requests found',type: LeaveRequestEntity, isArray: true })
@ApiResponse({ status: 400, description: 'Leave request not found' })
findAll(): Promise<LeaveRequests[]> { findAll(): Promise<LeaveRequests[]> {
return this.leaveRequetsService.findAll(); return this.leaveRequetsService.findAll();
} }
@Get(':id') @Get(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
@ApiOperation({summary: 'Find leave request' })
@ApiResponse({ status: 201, description: 'Leave request found',type: LeaveRequestEntity })
@ApiResponse({ status: 400, description: 'Leave request not found' })
findOne(@Param('id', ParseIntPipe) id: number): Promise<LeaveRequests> { findOne(@Param('id', ParseIntPipe) id: number): Promise<LeaveRequests> {
return this.leaveRequetsService.findOne(id); return this.leaveRequetsService.findOne(id);
} }
@Patch(':id') @Patch(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
update( @ApiOperation({summary: 'Update leave request' })
@Param('id', ParseIntPipe) id: number, @ApiResponse({ status: 201, description: 'Leave request updated',type: LeaveRequestEntity })
@Body() dto: UpdateLeaveRequestsDto, @ApiResponse({ status: 400, description: 'Leave request not found' })
): Promise<LeaveRequests> { update(@Param('id', ParseIntPipe) id: number,@Body() dto: UpdateLeaveRequestsDto): Promise<LeaveRequests> {
return this.leaveRequetsService.update(id, dto); return this.leaveRequetsService.update(id, dto);
} }
@Delete(':id') @Delete(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
@ApiOperation({summary: 'Delete leave request' })
@ApiResponse({ status: 201, description: 'Leave request deleted',type: LeaveRequestEntity })
@ApiResponse({ status: 400, description: 'Leave request not found' })
remove(@Param('id', ParseIntPipe) id: number): Promise<LeaveRequests> { remove(@Param('id', ParseIntPipe) id: number): Promise<LeaveRequests> {
return this.leaveRequetsService.remove(id); return this.leaveRequetsService.remove(id);
} }

View File

@ -1,27 +1,52 @@
import { ApiProperty } from "@nestjs/swagger";
import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { IsEnum, IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator"; import { IsEnum, IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator";
export class CreateLeaveRequestsDto { export class CreateLeaveRequestsDto {
@ApiProperty({
example: '4655867',
description: 'Employee`s id',
})
@Type(()=> Number) @Type(()=> Number)
@IsInt() @IsInt()
employee_id: number; employee_id: number;
@ApiProperty({
example: 'Sick or Vacation or Unpaid or Bereavement or Parental or Legal',
description: 'type of leave request for an accounting perception',
})
@IsEnum(LeaveTypes) @IsEnum(LeaveTypes)
leave_type: LeaveTypes; leave_type: LeaveTypes;
@ApiProperty({
example: '22/06/2463',
description: 'Leave request`s start date',
})
@Type(()=>Date) @Type(()=>Date)
@IsNotEmpty() @IsNotEmpty()
start_date_time:Date; start_date_time:Date;
@ApiProperty({
example: '25/03/3019',
description: 'Leave request`s end date',
})
@Type(()=>Date) @Type(()=>Date)
@IsOptional() @IsOptional()
end_date_time?: Date; end_date_time?: Date;
@ApiProperty({
example: 'My precious',
description: 'Leave request`s comment',
})
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
comment: string; comment: string;
@ApiProperty({
example: 'True or False or Pending or Denied or Cancelled or Escalated',
description: 'Leave request`s approval status',
})
@IsEnum(LeaveApprovalStatus) @IsEnum(LeaveApprovalStatus)
@IsOptional() @IsOptional()
approval_status?: LeaveApprovalStatus; approval_status?: LeaveApprovalStatus;

View File

@ -0,0 +1,49 @@
import { ApiProperty } from '@nestjs/swagger';
import { LeaveApprovalStatus, LeaveTypes } from '@prisma/client';
export class LeaveRequestEntity {
@ApiProperty({
example: 1,
description: 'Leave request`s unique id(auto-incremented)',
})
id: number;
@ApiProperty({
example: 42,
description: 'ID of concerned employee',
})
employee_id: number;
@ApiProperty({
example: 'SICK',
enum: LeaveTypes,
description: 'type of leave request for an accounting perception',
})
leave_type: LeaveTypes;
@ApiProperty({
example: '22/06/2463',
description: 'Leave request`s start date',
})
start_date_time: Date;
@ApiProperty({
example: '25/03/3019',
description: 'Leave request`s end date (optionnal)',
required: false,
})
end_date_time?: Date;
@ApiProperty({
example: 'My precious',
description: 'Leave request employee`s comment',
})
comment: string;
@ApiProperty({
example: 'PENDING',
enum: LeaveApprovalStatus,
description: 'Leave request`s approval status',
})
approval_status: LeaveApprovalStatus;
}

View File

@ -1,41 +1,62 @@
import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common'; import { Body, Controller, Delete, Get, Param, Patch, Post, UseGuards } from '@nestjs/common';
import { OauthAccessTokensService } from '../services/oauth-access-tokens.service'; import { OauthAccessTokensService } from '../services/oauth-access-tokens.service';
import { CreateOauthAccessTokenDto } from '../dtos/create-oauth-access-token.dto'; import { CreateOauthAccessTokenDto } from '../dtos/create-oauth-access-token.dto';
import { OAuthAccessTokens } from '@prisma/client'; import { OAuthAccessTokens } from '@prisma/client';
import { UpdateOauthAccessTokenDto } from '../dtos/update-oauth-access-token.dto'; import { UpdateOauthAccessTokenDto } from '../dtos/update-oauth-access-token.dto';
import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { Roles as RoleEnum } from '.prisma/client'; 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 { OAuthAccessTokenEntity } from '../dtos/swagger-entities/oauth-access-token.entity';
@ApiTags('OAuth Access Tokens')
@ApiBearerAuth('access-token')
@UseGuards(JwtAuthGuard)
@Controller('oauth-access-tokens') @Controller('oauth-access-tokens')
export class OauthAccessTokensController { export class OauthAccessTokensController {
constructor(private readonly oauthAccessTokensService: OauthAccessTokensService){} constructor(private readonly oauthAccessTokensService: OauthAccessTokensService){}
@Post() @Post()
@RolesAllowed(RoleEnum.ADMIN) @RolesAllowed(RoleEnum.ADMIN)
@ApiOperation({summary: 'Create OAuth access token' })
@ApiResponse({ status: 201, description: 'OAuth access token created', type: OAuthAccessTokenEntity })
@ApiResponse({ status: 400, description: 'Incomplete task or invalid data' })
create(@Body()dto: CreateOauthAccessTokenDto): Promise<OAuthAccessTokens> { create(@Body()dto: CreateOauthAccessTokenDto): Promise<OAuthAccessTokens> {
return this.oauthAccessTokensService.create(dto); return this.oauthAccessTokensService.create(dto);
} }
@Get() @Get()
@RolesAllowed(RoleEnum.ADMIN) @RolesAllowed(RoleEnum.ADMIN)
@ApiOperation({summary: 'Find all OAuth access token' })
@ApiResponse({ status: 201, description: 'List of OAuth access token found', type: OAuthAccessTokenEntity, isArray: true })
@ApiResponse({ status: 400, description: 'List of OAuth access token not found' })
findAll(): Promise<OAuthAccessTokens[]> { findAll(): Promise<OAuthAccessTokens[]> {
return this.oauthAccessTokensService.findAll(); return this.oauthAccessTokensService.findAll();
} }
@Get(':id') @Get(':id')
@RolesAllowed(RoleEnum.ADMIN) @RolesAllowed(RoleEnum.ADMIN)
@ApiOperation({summary: 'Find OAuth access token' })
@ApiResponse({ status: 201, description: 'OAuth access token found', type: OAuthAccessTokenEntity })
@ApiResponse({ status: 400, description: 'OAuth access token not found' })
findOne(@Param('id') id: string): Promise<OAuthAccessTokens> { findOne(@Param('id') id: string): Promise<OAuthAccessTokens> {
return this.oauthAccessTokensService.findOne(id); return this.oauthAccessTokensService.findOne(id);
} }
@Patch(':id') @Patch(':id')
@RolesAllowed(RoleEnum.ADMIN) @RolesAllowed(RoleEnum.ADMIN)
@ApiOperation({summary: 'Update OAuth access token' })
@ApiResponse({ status: 201, description: 'OAuth access token updated', type: OAuthAccessTokenEntity })
@ApiResponse({ status: 400, description: 'OAuth access token not found' })
update(@Param('id') id: string, @Body() dto: UpdateOauthAccessTokenDto): Promise<OAuthAccessTokens> { update(@Param('id') id: string, @Body() dto: UpdateOauthAccessTokenDto): Promise<OAuthAccessTokens> {
return this.oauthAccessTokensService.update(id,dto); return this.oauthAccessTokensService.update(id,dto);
} }
@Delete(':id') @Delete(':id')
@RolesAllowed(RoleEnum.ADMIN) @RolesAllowed(RoleEnum.ADMIN)
@ApiOperation({summary: 'Delete OAuth access token' })
@ApiResponse({ status: 201, description: 'OAuth access token deleted', type: OAuthAccessTokenEntity })
@ApiResponse({ status: 400, description: 'OAuth access token not found' })
remove(@Param('id') id: string): Promise<OAuthAccessTokens> { remove(@Param('id') id: string): Promise<OAuthAccessTokens> {
return this.oauthAccessTokensService.remove(id); return this.oauthAccessTokensService.remove(id);
} }

View File

@ -1,29 +1,60 @@
import { ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { IsArray, IsDate, IsOptional, IsString, IsUUID } from "class-validator"; import { IsArray, IsDate, IsOptional, IsString, IsUUID } from "class-validator";
export class CreateOauthAccessTokenDto { export class CreateOauthAccessTokenDto {
@ApiProperty({
example: 'S7A2U8R7O6N6',
description: 'User`s unique identification number',
})
@IsUUID() @IsUUID()
user_id: string; user_id: string;
@ApiProperty({
example: 'app.targo.ca',
description: 'URL in which the access token is used for',
})
@IsString() @IsString()
application: string; application: string;
@ApiProperty({
example: 'L5O6R4D3/O6F3#T8H4E3&R6I4N6G4S7 ...',
description: 'Access token',
})
@IsString() @IsString()
access_token: string; access_token: string;
@ApiProperty({
example: 'Th3731102h1p07Th3R1n92',
description: 'Refresh token',
})
@IsString() @IsString()
refresh_token: string; refresh_token: string;
@ApiProperty({
example: '25/12/3018',
description: 'Access token`s expiry date',
})
@Type(()=> Date) @Type(()=> Date)
@IsDate() @IsDate()
access_token_expiry: Date; access_token_expiry: Date;
@ApiProperty({
example: '26/02/3019',
description: 'Refresh token`s expiry date',
required: false,
})
@Type(()=> Date) @Type(()=> Date)
@IsDate() @IsDate()
@IsOptional() @IsOptional()
refresh_token_expiry?: Date; refresh_token_expiry?: Date;
@ApiProperty({
example: 'access tolkiens, email, etc... ',
description: 'scopes of infos linked to the access token',
required: false,
})
@IsArray() @IsArray()
@IsString() @IsString()
@IsOptional() @IsOptional()

View File

@ -0,0 +1,72 @@
import { ApiProperty } from '@nestjs/swagger';
export class OAuthAccessTokenEntity {
@ApiProperty({
example: 'cklwi0vb70000z2z20q6f19qk',
description: 'Unique ID of an OAuth token (auto-generated)',
})
id: string;
@ApiProperty({
example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d',
description: 'UUID User`s unique identification number',
})
user_id: string;
@ApiProperty({
example: 'app.targo.ca',
description: 'URL in which the access token is used for',
})
application: string;
@ApiProperty({
example: 'L5O6R4D3/O6F3#T8H4E3&R6I4N6G4S7',
description: 'Access token',
})
access_token: string;
@ApiProperty({
example: 'Th3731102h1p07Th3R1n92',
description: 'Refresh token',
})
refresh_token: string;
@ApiProperty({
example: '3018-12-25T00:00:00.000Z',
description: 'Access token`s expiry date',
})
access_token_expiry: Date;
@ApiProperty({
example: '3019-02-26T00:00:00.000Z',
description: 'Refresh token`s expiry date (optional)',
required: false,
})
refresh_token_expiry?: Date;
@ApiProperty({
example: ['email', 'profile', 'access_tolkiens'],
description: 'scopes of infos linked to the access token',
required: false,
})
scopes: string[];
@ApiProperty({
example: false,
description: 'revoke status',
})
is_revoked: boolean;
@ApiProperty({
example: '2025-07-22',
description: 'creation date',
})
created_at: Date;
@ApiProperty({
example: '2025-07-23',
description: 'Latest update (optional)',
required: false,
})
updated_at?: Date;
}

View File

@ -1,35 +1,53 @@
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common";
import { ShiftCodesService } from "../services/shift-codes.service"; import { ShiftCodesService } from "../services/shift-codes.service";
import { CreateShiftCodesDto } from "../dtos/create-shift-codes.dto"; import { CreateShiftCodesDto } from "../dtos/create-shift-codes.dto";
import { ShiftCodes } from "@prisma/client"; import { ShiftCodes } from "@prisma/client";
import { UpdateShiftCodesDto } from "../dtos/update-shift-codes.dto"; import { UpdateShiftCodesDto } from "../dtos/update-shift-codes.dto";
import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { Roles as RoleEnum } from '.prisma/client'; 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 { ShiftCodesEntity } from "../dtos/swagger-entities/shift-codes.entity";
@Controller() @ApiTags('Shift Codes')
@ApiBearerAuth('access-token')
@UseGuards(JwtAuthGuard)
@Controller('shift-codes')
export class ShiftCodesController { export class ShiftCodesController {
constructor(private readonly shiftCodesService: ShiftCodesService) {} constructor(private readonly shiftCodesService: ShiftCodesService) {}
@Post() @Post()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR)
@ApiOperation({ summary: 'Create shift code' })
@ApiResponse({ status: 201, description: 'Shift code created',type: ShiftCodesEntity })
@ApiResponse({ status: 400, description: 'Incomplete task or invalid data' })
create(@Body()dto: CreateShiftCodesDto): Promise<ShiftCodes> { create(@Body()dto: CreateShiftCodesDto): Promise<ShiftCodes> {
return this.shiftCodesService.create(dto); return this.shiftCodesService.create(dto);
} }
@Get() @Get()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR)
@ApiOperation({ summary: 'Find all shift codes' })
@ApiResponse({ status: 201, description: 'List of shift codes found',type: ShiftCodesEntity, isArray: true })
@ApiResponse({ status: 400, description: 'List of shift codes not found' })
findAll(): Promise<ShiftCodes[]> { findAll(): Promise<ShiftCodes[]> {
return this.shiftCodesService.findAll(); return this.shiftCodesService.findAll();
} }
@Get(':id') @Get(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR)
@ApiOperation({ summary: 'Find shift code' })
@ApiResponse({ status: 201, description: 'Shift code found',type: ShiftCodesEntity })
@ApiResponse({ status: 400, description: 'Shift code not found' })
findOne(@Param('id', ParseIntPipe) id: number): Promise<ShiftCodes> { findOne(@Param('id', ParseIntPipe) id: number): Promise<ShiftCodes> {
return this.shiftCodesService.findOne(id); return this.shiftCodesService.findOne(id);
} }
@Patch(':id') @Patch(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR)
@ApiOperation({ summary: 'Update shift code' })
@ApiResponse({ status: 201, description: 'Shift code updated',type: ShiftCodesEntity })
@ApiResponse({ status: 400, description: 'Shift code not found' })
update(@Param('id', ParseIntPipe) id: number, update(@Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateShiftCodesDto): Promise<ShiftCodes> { @Body() dto: UpdateShiftCodesDto): Promise<ShiftCodes> {
return this.shiftCodesService.update(id,dto); return this.shiftCodesService.update(id,dto);
@ -37,6 +55,9 @@ export class ShiftCodesController {
@Delete(':id') @Delete(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR)
@ApiOperation({ summary: 'Delete shift code' })
@ApiResponse({ status: 201, description: 'Shift code deleted',type: ShiftCodesEntity })
@ApiResponse({ status: 400, description: 'Shift code not found' })
remove(@Param('id', ParseIntPipe)id: number): Promise<ShiftCodes> { remove(@Param('id', ParseIntPipe)id: number): Promise<ShiftCodes> {
return this.shiftCodesService.remove(id); return this.shiftCodesService.remove(id);
} }

View File

@ -1,10 +1,20 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsNotEmpty, IsString } from "class-validator"; import { IsNotEmpty, IsString } from "class-validator";
export class CreateShiftCodesDto { export class CreateShiftCodesDto {
@ApiProperty({
example: 'Regular or Night or Emergency, etc...',
description: 'Type of shifts for an account perception',
})
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
shift_type: string; shift_type: string;
@ApiProperty({
example: 'G1, G2, G3, etc...',
description: 'bank`s code related to the type of shift',
})
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
bank_code: string; bank_code: string;

View File

@ -0,0 +1,21 @@
import { ApiProperty } from '@nestjs/swagger';
export class ShiftCodesEntity {
@ApiProperty({
example: 1,
description: 'Unique ID of a shift-code (auto-generated)',
})
id: number;
@ApiProperty({
example: 'Night',
description: 'Type of shifts for an account perception',
})
shift_type: string;
@ApiProperty({
example: 'G2',
description: 'bank`s code related to the type of shift',
})
bank_code: string;
}

View File

@ -1,45 +1,62 @@
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common";
import { ShiftsService } from "../services/shifts.service"; import { ShiftsService } from "../services/shifts.service";
import { Shifts } from "@prisma/client"; import { Shifts } from "@prisma/client";
import { CreateShiftDto } from "../dtos/create-shifts.dto"; import { CreateShiftDto } from "../dtos/create-shifts.dto";
import { UpdateShiftsDto } from "../dtos/update-shifts.dto"; import { UpdateShiftsDto } from "../dtos/update-shifts.dto";
import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { Roles as RoleEnum } from '.prisma/client'; 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";
@ApiTags('Shifts')
@ApiBearerAuth('access-token')
@UseGuards(JwtAuthGuard)
@Controller('shifts') @Controller('shifts')
export class ShiftsController { export class ShiftsController {
constructor(private readonly shiftsService: ShiftsService){} constructor(private readonly shiftsService: ShiftsService){}
@Post() @Post()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) @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> { create(@Body() dto: CreateShiftDto): Promise<Shifts> {
return this.shiftsService.create(dto); return this.shiftsService.create(dto);
} }
@Get() @Get()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) @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[]> { findAll(): Promise<Shifts[]> {
return this.shiftsService.findAll(); return this.shiftsService.findAll();
} }
@Get(':id') @Get(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) @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> { findOne(@Param('id', ParseIntPipe) id: number): Promise<Shifts> {
return this.shiftsService.findOne(id); return this.shiftsService.findOne(id);
} }
@Patch(':id') @Patch(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
update( @ApiOperation({ summary: 'Update shift' })
@Param('id', ParseIntPipe) id: number, @ApiResponse({ status: 201, description: 'Shift updated',type: ShiftEntity })
@Body() dto: UpdateShiftsDto, @ApiResponse({ status: 400, description: 'Shift not found' })
): Promise<Shifts> { update(@Param('id', ParseIntPipe) id: number,@Body() dto: UpdateShiftsDto): Promise<Shifts> {
return this.shiftsService.update(id, dto); return this.shiftsService.update(id, dto);
} }
@Delete(':id') @Delete(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) @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> { remove(@Param('id', ParseIntPipe) id: number): Promise<Shifts> {
return this.shiftsService.remove(id); return this.shiftsService.remove(id);
} }

View File

@ -1,26 +1,47 @@
import { ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { IsDate, IsDateString, IsInt } from "class-validator"; import { IsDate, IsDateString, IsInt } from "class-validator";
export class CreateShiftDto { export class CreateShiftDto {
@ApiProperty({
example: 'Th3F3110w5h1pX2024',
description: 'ID number for a set timesheet',
})
@Type(() => Number) @Type(() => Number)
@IsInt() @IsInt()
timesheet_id: number; timesheet_id: number;
@ApiProperty({
example: '0n3R1n962Ru13xX',
description: 'ID number of a shift code (link with shift-codes)',
})
@Type(() => Number) @Type(() => Number)
@IsInt() @IsInt()
shift_code_id: number; shift_code_id: number;
@ApiProperty({
example: '20/10/3018',
description: 'Date where the shift takes place',
})
@IsDateString() @IsDateString()
@Type(() => Date) @Type(() => Date)
@IsDate() @IsDate()
date: Date; date: Date;
@ApiProperty({
example: '08:00',
description: 'Start time of the said shift',
})
@IsDateString() @IsDateString()
@Type(() => Date) @Type(() => Date)
@IsDate() @IsDate()
start_time: Date; start_time: Date;
@ApiProperty({
example: '17:00',
description: 'End time of the said shift',
})
@IsDateString() @IsDateString()
@Type(() => Date) @Type(() => Date)
@IsDate() @IsDate()

View File

@ -0,0 +1,39 @@
import { ApiProperty } from '@nestjs/swagger';
export class ShiftEntity {
@ApiProperty({
example: 1,
description: 'Unique ID of the shift (auto-generated)',
})
id: number;
@ApiProperty({
example: 101,
description: 'ID number for a set timesheet',
})
timesheet_id: number;
@ApiProperty({
example: 7,
description: 'ID number of a shift code (link with shift-codes)',
})
shift_code_id: number;
@ApiProperty({
example: '3018-10-20T00:00:00.000Z',
description: 'Date where the shift takes place',
})
date: Date;
@ApiProperty({
example: '3018-10-20T08:00:00.000Z',
description: 'Start time of the said shift',
})
start_time: Date;
@ApiProperty({
example: '3018-10-20T17:00:00.000Z',
description: 'End time of the said shift',
})
end_time: Date;
}

View File

@ -1,35 +1,53 @@
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from '@nestjs/common';
import { TimesheetsService } from '../services/timesheets.service'; import { TimesheetsService } from '../services/timesheets.service';
import { CreateTimesheetDto } from '../dtos/create-timesheet.dto'; import { CreateTimesheetDto } from '../dtos/create-timesheet.dto';
import { Timesheets } from '@prisma/client'; import { Timesheets } from '@prisma/client';
import { UpdateTimesheetDto } from '../dtos/update-timesheet.dto'; import { UpdateTimesheetDto } from '../dtos/update-timesheet.dto';
import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { Roles as RoleEnum } from '.prisma/client'; 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 { TimesheetEntity } from '../dtos/swagger-entities/timesheet.entity';
@ApiTags('Timesheets')
@ApiBearerAuth('access-token')
@UseGuards(JwtAuthGuard)
@Controller('timesheets') @Controller('timesheets')
export class TimesheetsController { export class TimesheetsController {
constructor(private readonly timesheetsService: TimesheetsService) {} constructor(private readonly timesheetsService: TimesheetsService) {}
@Post() @Post()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
@ApiOperation({ summary: 'Create timesheet' })
@ApiResponse({ status: 201, description: 'Timesheet created', type: TimesheetEntity })
@ApiResponse({ status: 400, description: 'Incomplete task or invalid data' })
create(@Body() dto: CreateTimesheetDto): Promise<Timesheets> { create(@Body() dto: CreateTimesheetDto): Promise<Timesheets> {
return this.timesheetsService.create(dto); return this.timesheetsService.create(dto);
} }
@Get() @Get()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
@ApiOperation({ summary: 'Find all timesheets' })
@ApiResponse({ status: 201, description: 'List of timesheet found', type: TimesheetEntity, isArray: true })
@ApiResponse({ status: 400, description: 'List of timesheets not found' })
findAll(): Promise<Timesheets[]> { findAll(): Promise<Timesheets[]> {
return this.timesheetsService.findAll(); return this.timesheetsService.findAll();
} }
@Get(':id') @Get(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
@ApiOperation({ summary: 'Find timesheet' })
@ApiResponse({ status: 201, description: 'Timesheet found', type: TimesheetEntity })
@ApiResponse({ status: 400, description: 'Timesheet not found' })
findOne(@Param('id', ParseIntPipe) id: number): Promise<Timesheets> { findOne(@Param('id', ParseIntPipe) id: number): Promise<Timesheets> {
return this.timesheetsService.findOne(id); return this.timesheetsService.findOne(id);
} }
@Patch(':id') @Patch(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
@ApiOperation({ summary: 'Update timesheet' })
@ApiResponse({ status: 201, description: 'Timesheet updated', type: TimesheetEntity })
@ApiResponse({ status: 400, description: 'Timesheet not found' })
update( update(
@Param('id', ParseIntPipe) id:number, @Param('id', ParseIntPipe) id:number,
@Body() dto: UpdateTimesheetDto, @Body() dto: UpdateTimesheetDto,
@ -39,6 +57,9 @@ export class TimesheetsController {
@Delete(':id') @Delete(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
@ApiOperation({ summary: 'Delete timesheet' })
@ApiResponse({ status: 201, description: 'Timesheet deleted', type: TimesheetEntity })
@ApiResponse({ status: 400, description: 'Timesheet not found' })
remove(@Param('id', ParseIntPipe) id: number): Promise<Timesheets> { remove(@Param('id', ParseIntPipe) id: number): Promise<Timesheets> {
return this.timesheetsService.remove(id); return this.timesheetsService.remove(id);
} }

View File

@ -1,12 +1,21 @@
import { ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { IsBoolean, IsInt, IsOptional } from "class-validator"; import { IsBoolean, IsInt, IsOptional } from "class-validator";
export class CreateTimesheetDto { export class CreateTimesheetDto {
@ApiProperty({
example: '426433',
description: 'employee`s ID number of linked timsheet',
})
@Type(() => Number) @Type(() => Number)
@IsInt() @IsInt()
employee_id: number; employee_id: number;
@ApiProperty({
example: 'True or False or Pending or Denied or Cancelled or Escalated',
description: 'Timesheet`s approval status',
})
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
is_approved?: boolean; is_approved?: boolean;

View File

@ -0,0 +1,21 @@
import { ApiProperty } from '@nestjs/swagger';
export class TimesheetEntity {
@ApiProperty({
example: 1,
description: 'timesheet`s unique ID (auto-generated)',
})
id: number;
@ApiProperty({
example: 426433,
description: 'employee`s ID number of linked timsheet',
})
employee_id: number;
@ApiProperty({
example: true,
description: 'Timesheet`s approval status',
})
is_approved: boolean;
}

View File

@ -0,0 +1,48 @@
import { ApiProperty } from '@nestjs/swagger';
import { Roles } from '@prisma/client';
export class UserEntity {
@ApiProperty({
example: 'd67f05be-6dd1-464f-b5f7-31b325e21b4a',
description: 'User`s unique UUID (primary key)',
})
id: string;
@ApiProperty({
example: 'Aragorn',
description: 'user`s first name',
})
first_name: string;
@ApiProperty({
example: 'Elessar',
description: 'user`s last name',
})
last_name: string;
@ApiProperty({
example: 'king@arnor-gondor.gov',
description: 'Unique email address',
})
email: string;
@ApiProperty({
example: 5141234567,
description: 'Unique phone number',
})
phone_number: number;
@ApiProperty({
example: 'Minas Tirith, Gondor',
description: 'residence address (optional)',
required: false,
})
residence?: string;
@ApiProperty({
example: 'EMPLOYEE',
enum: Roles,
description: 'User`s given role',
})
role: Roles;
}