Merge branch 'main' of git.targo.ca:Targo/targo_backend

This commit is contained in:
Nicolas Drolet 2025-12-19 15:39:24 -05:00
commit 072855f34d
17 changed files with 64 additions and 84 deletions

View File

@ -17,8 +17,8 @@ export class AuthController {
@Get('/callback') @Get('/callback')
@UseGuards(OIDCLoginGuard) @UseGuards(OIDCLoginGuard)
loginCallback(@Req() req: Request, @Res() res: Response) { loginCallback(@Req() req: Request, @Res() res: Response) {
// res.redirect('http://10.100.251.2:9011/#/login-success'); res.redirect("http://10.100.251.2:9013/#/v1/login-success");
res.redirect('http://localhost:9000/#/login-success'); // res.redirect(process.env.REDIRECT_URL_DEV!);
} }
@Get('/me') @Get('/me')

View File

@ -23,7 +23,7 @@ export class EmployeesController {
} }
@Get('profile') @Get('profile')
@ModuleAccessAllowed(ModulesEnum.employee_management) @ModuleAccessAllowed(ModulesEnum.personal_profile)
async findProfile(@Access('email') email: string, @Query('employee_email') employee_email?: string, async findProfile(@Access('email') email: string, @Query('employee_email') employee_email?: string,
): Promise<Result<Partial<EmployeeDetailedDto>, string>> { ): Promise<Result<Partial<EmployeeDetailedDto>, string>> {
return await this.getService.findOneDetailedProfile(email, employee_email); return await this.getService.findOneDetailedProfile(email, employee_email);

View File

@ -1,6 +1,6 @@
import { Controller, Get } from "@nestjs/common"; import { Controller, Get } from "@nestjs/common";
import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators"; import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators";
import { HomePageService } from "src/identity-and-account/home-page/home-page.service"; import { HomePageService } from "src/identity-and-account/help/home-page.service";
import { Modules as ModulesEnum } from ".prisma/client"; import { Modules as ModulesEnum } from ".prisma/client";
import { Access } from "src/common/decorators/module-access.decorators"; import { Access } from "src/common/decorators/module-access.decorators";

View File

@ -1,7 +1,7 @@
import { Module } from "@nestjs/common"; import { Module } from "@nestjs/common";
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
import { HomePageController } from "src/identity-and-account/home-page/home-page.controller"; import { HomePageController } from "src/identity-and-account/help/home-page.controller";
import { HomePageService } from "src/identity-and-account/home-page/home-page.service"; import { HomePageService } from "src/identity-and-account/help/home-page.service";
@Module({ @Module({
controllers: [HomePageController], controllers: [HomePageController],

View File

@ -30,7 +30,7 @@ export class HomePageService {
const help_message: string[] = []; const help_message: string[] = [];
if (module_access.dashboard) help_message.push('dashboard'); if (module_access.dashboard) help_message.push('dashboard');
if (module_access.personal_profile) help_message.push('personnal_profile'); if (module_access.personal_profile) help_message.push('personal_profile');
if (module_access.timesheets) help_message.push('timesheets'); if (module_access.timesheets) help_message.push('timesheets');
if (module_access.employee_list) help_message.push('employee_list'); if (module_access.employee_list) help_message.push('employee_list');
if (module_access.employee_management) help_message.push('employee_management'); if (module_access.employee_management) help_message.push('employee_management');

View File

@ -12,11 +12,11 @@ import { AccessGetService } from "src/identity-and-account/user-module-access/se
import { AccessUpdateService } from "src/identity-and-account/user-module-access/services/module-access-update.service"; import { AccessUpdateService } from "src/identity-and-account/user-module-access/services/module-access-update.service";
import { UsersService } from "src/identity-and-account/users-management/services/users.service"; import { UsersService } from "src/identity-and-account/users-management/services/users.service";
import { UsersModule } from "src/identity-and-account/users-management/users.module"; import { UsersModule } from "src/identity-and-account/users-management/users.module";
import { HomePageModule } from "src/identity-and-account/home-page/home-page.module"; import { HomePageModule } from "src/identity-and-account/help/home-page.module";
import { EmployeesCreateService } from "src/identity-and-account/employees/services/employees-create.service"; import { EmployeesCreateService } from "src/identity-and-account/employees/services/employees-create.service";
import { EmployeesUpdateService } from "src/identity-and-account/employees/services/employees-update.service"; import { EmployeesUpdateService } from "src/identity-and-account/employees/services/employees-update.service";
import { HomePageController } from "src/identity-and-account/home-page/home-page.controller"; import { HomePageController } from "src/identity-and-account/help/home-page.controller";
import { HomePageService } from "src/identity-and-account/home-page/home-page.service"; import { HomePageService } from "src/identity-and-account/help/home-page.service";
@Module({ @Module({
imports: [ imports: [

View File

@ -4,14 +4,12 @@ if (!(globalThis as any).crypto) {
(globalThis as any).crypto = nodeCrypto; (globalThis as any).crypto = nodeCrypto;
} }
import { ensureAttachmentsTmpDir } from './time-and-attendance/attachments/config/attachment.fs'; import { ensureAttachmentsTmpDir } from './time-and-attendance/attachments/config/attachment.fs';
import { resolveAttachmentsRoot } from './time-and-attendance/attachments/config/attachment.config';// log to be removed post dev
import { ATT_TMP_DIR } from './time-and-attendance/attachments/config/attachment.config'; // log to be removed post dev
import { NestFactory, Reflector } from '@nestjs/core'; import { NestFactory, Reflector } from '@nestjs/core';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
// import { JwtAuthGuard } from './modules/authentication/guards/jwt-auth.guard'; // import { JwtAuthGuard } from './modules/authentication/guards/jwt-auth.guard';
import { ModulesGuard } from './common/guards/modules.guard'; import { ModulesGuard } from './common/guards/modules.guard';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; // import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { writeFileSync } from 'fs'; // import { writeFileSync } from 'fs';
import * as session from 'express-session'; import * as session from 'express-session';
import * as passport from 'passport'; import * as passport from 'passport';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
@ -59,40 +57,35 @@ async function bootstrap() {
credentials: true, credentials: true,
}); });
//swagger config // //swagger config
const config = new DocumentBuilder() // const config = new DocumentBuilder()
.setTitle('Targo_Backend') // .setTitle('Targo_Backend')
.setDescription('Documentation de l`API REST pour Targo (NestJS + Prisma)') // .setDescription('Documentation de l`API REST pour Targo (NestJS + Prisma)')
.setVersion('1.0') // .setVersion('1.0')
.addBearerAuth({ // .addBearerAuth({
type: 'http', // type: 'http',
scheme: 'bearer', // scheme: 'bearer',
bearerFormat: 'JWT', // bearerFormat: 'JWT',
name: 'Authorization', // name: 'Authorization',
description: 'Invalid JWT token', // description: 'Invalid JWT token',
in: 'header', // in: 'header',
}, 'access-token') // }, 'access-token')
.addTag('Users') // .addTag('Users')
.addTag('Employees') // .addTag('Employees')
.addTag('Customers') // .addTag('Customers')
.addTag('Timesheets') // .addTag('Timesheets')
.addTag('Shifts') // .addTag('Shifts')
.addTag('Leave Requests') // .addTag('Leave Requests')
.addTag('Shift Codes') // .addTag('Shift Codes')
.addTag('OAuth Access Tokens') // .addTag('OAuth Access Tokens')
.addTag('Authorization') // .addTag('Authorization')
.build(); // .build();
//document builder for swagger docs // //document builder for swagger docs
const documentFactory = () => SwaggerModule.createDocument(app, config); // const documentFactory = () => SwaggerModule.createDocument(app, config);
const document = documentFactory() // const document = documentFactory()
SwaggerModule.setup('api/docs', app, document); // SwaggerModule.setup('api/docs', app, document);
writeFileSync('./docs/swagger/swagger-spec.json', JSON.stringify(document, null, 2)); // writeFileSync('./docs/swagger/swagger-spec.json', JSON.stringify(document, null, 2));
// logs to be removed post dev
console.log('[ENV.ATTACHMENTS_ROOT]', process.env.ATTACHMENTS_ROOT);
console.log('[resolveAttachmentsRoot()]', resolveAttachmentsRoot());
console.log('[ATT_TMP_DIR()]', ATT_TMP_DIR());
await ensureAttachmentsTmpDir(); await ensureAttachmentsTmpDir();
await app.listen(process.env.PORT ?? 3000); await app.listen(process.env.PORT ?? 3000);

View File

@ -13,20 +13,17 @@ export class BankCodesControllers {
//_____________________________________________________________________________________________ //_____________________________________________________________________________________________
@Post() @Post()
@ModuleAccessAllowed(ModulesEnum.employee_management)
create(@Body() dto: Prisma.BankCodesCreateInput create(@Body() dto: Prisma.BankCodesCreateInput
): Promise<Result<boolean, string>> { ): Promise<Result<boolean, string>> {
return this.bankCodesService.create(dto); return this.bankCodesService.create(dto);
} }
@Get() @Get()
@ModuleAccessAllowed(ModulesEnum.employee_management)
findAll() { findAll() {
return this.bankCodesService.findAll(); return this.bankCodesService.findAll();
} }
@Patch(':id') @Patch(':id')
@ModuleAccessAllowed(ModulesEnum.employee_management)
update(@Param('id', ParseIntPipe) id: number, @Body() dto: Prisma.BankCodesUpdateInput update(@Param('id', ParseIntPipe) id: number, @Body() dto: Prisma.BankCodesUpdateInput
): Promise<Result<boolean, string>> { ): Promise<Result<boolean, string>> {
return this.bankCodesService.update(id, dto) return this.bankCodesService.update(id, dto)

View File

@ -17,7 +17,7 @@ export class PayPeriodsController {
) { } ) { }
@Get('current-and-all') @Get('current-and-all')
@ModuleAccessAllowed(ModulesEnum.timesheets_approval) @ModuleAccessAllowed(ModulesEnum.timesheets)
async getCurrentAndAll(@Query('date') date?: string): Promise<Result<PayPeriodBundleDto, string>> { async getCurrentAndAll(@Query('date') date?: string): Promise<Result<PayPeriodBundleDto, string>> {
const current = await this.queryService.findCurrent(date); const current = await this.queryService.findCurrent(date);
if (!current.success) return { success: false, error: 'INVALID_PAY_PERIOD' }; if (!current.success) return { success: false, error: 'INVALID_PAY_PERIOD' };
@ -29,13 +29,13 @@ export class PayPeriodsController {
} }
@Get("date/:date") @Get("date/:date")
@ModuleAccessAllowed(ModulesEnum.timesheets_approval) @ModuleAccessAllowed(ModulesEnum.timesheets)
async findByDate(@Param("date") date: string) { async findByDate(@Param("date") date: string) {
return this.queryService.findByDate(date); return this.queryService.findByDate(date);
} }
@Get(":year/:periodNumber") @Get(":year/:periodNumber")
@ModuleAccessAllowed(ModulesEnum.timesheets_approval) @ModuleAccessAllowed(ModulesEnum.timesheets)
async findOneByYear( async findOneByYear(
@Param("year", ParseIntPipe) year: number, @Param("year", ParseIntPipe) year: number,
@Param("periodNumber", ParseIntPipe) period_no: number, @Param("periodNumber", ParseIntPipe) period_no: number,

View File

@ -43,7 +43,7 @@ export class SchedulePresetsController {
@Delete('delete/:id') @Delete('delete/:id')
@ModuleAccessAllowed(ModulesEnum.employee_management) @ModuleAccessAllowed(ModulesEnum.employee_management)
async deletePreset( async deletePreset(
@Param('id', ParseIntPipe) id: number) { @Param('id') id: number) {
return await this.deleteService.deletePreset(id); return await this.deleteService.deletePreset(id);
} }

View File

@ -5,7 +5,6 @@ import { HH_MM_REGEX } from "src/common/utils/constants.utils";
export class SchedulePresetsDto { export class SchedulePresetsDto {
@IsInt() id!: number; @IsInt() id!: number;
@IsString() name!: string; @IsString() name!: string;
@IsBoolean() @IsOptional() is_default: boolean;
@IsArray() @ArrayMinSize(1) shifts: SchedulePresetShiftsDto[]; @IsArray() @ArrayMinSize(1) shifts: SchedulePresetShiftsDto[];
} }

View File

@ -32,7 +32,6 @@ export class SchedulePresetsApplyService {
schedule_preset: { schedule_preset: {
select: { select: {
id: true, id: true,
is_default: true,
shifts: true, shifts: true,
}, },
}, },
@ -94,7 +93,6 @@ export class SchedulePresetsApplyService {
schedule_preset: { schedule_preset: {
select: { select: {
id: true, id: true,
is_default: true,
shifts: { shifts: {
where: { week_day: $Enums.Weekday[week_day] }, where: { week_day: $Enums.Weekday[week_day] },
select: { select: {

View File

@ -56,17 +56,17 @@ export class SchedulePresetsCreateService {
await this.prisma.$transaction(async (tx) => { await this.prisma.$transaction(async (tx) => {
//check if employee chose this preset has a default preset and ensure all others are false //check if employee chose this preset has a default preset and ensure all others are false
if (dto.is_default) { // if (dto.is_default) {
await tx.schedulePresets.updateMany({ // await tx.schedulePresets.updateMany({
where: { is_default: true }, // where: { is_default: true },
data: { is_default: false }, // data: { is_default: false },
}); // });
} // }
await tx.schedulePresets.create({ await tx.schedulePresets.create({
data: { data: {
name: dto.name, name: dto.name,
is_default: dto.is_default ?? false, // is_default: dto.is_default ?? false,
shifts: { shifts: {
create: dto.shifts.map((shift, index) => { create: dto.shifts.map((shift, index) => {
//validated bank_codes sent as a Result Array to access its data //validated bank_codes sent as a Result Array to access its data

View File

@ -1,6 +1,8 @@
import { Injectable } from "@nestjs/common";
import { Result } from "src/common/errors/result-error.factory"; import { Result } from "src/common/errors/result-error.factory";
import { PrismaService } from "src/prisma/prisma.service"; import { PrismaService } from "src/prisma/prisma.service";
@Injectable()
export class SchedulePresetDeleteService { export class SchedulePresetDeleteService {
constructor(private readonly prisma: PrismaService) { } constructor(private readonly prisma: PrismaService) { }
@ -8,19 +10,22 @@ export class SchedulePresetDeleteService {
// DELETE // DELETE
//_________________________________________________________________ //_________________________________________________________________
async deletePreset(preset_id: number): Promise<Result<boolean, string>> { async deletePreset(preset_id: number): Promise<Result<boolean, string>> {
const preset = await this.prisma.schedulePresets.findFirst({ console.log('preset_id received: ', preset_id)
const preset = await this.prisma.schedulePresets.findUnique({
where: { id: preset_id }, where: { id: preset_id },
select: { id: true }, select: { id: true },
}); });
if (!preset) return { success: false, error: `SCHEDULE_PRESET_NOT_FOUND` }; if (!preset) return { success: false, error: `SCHEDULE_PRESET_NOT_FOUND` };
console.log('preset found: ', preset.id)
await this.prisma.employees.updateMany({
where: { schedule_preset_id: preset.id }, const updated_employees = await this.prisma.employees.updateMany({
where: { schedule_preset_id: preset_id },
data: { data: {
schedule_preset_id: null, schedule_preset_id: 0,
}, },
}); });
console.log('employee schedule id updated', updated_employees);
await this.prisma.$transaction(async (tx) => { await this.prisma.$transaction(async (tx) => {
await tx.schedulePresetShifts.deleteMany({ where: { preset_id: preset_id } }); await tx.schedulePresetShifts.deleteMany({ where: { preset_id: preset_id } });

View File

@ -15,7 +15,7 @@ export class SchedulePresetsGetService {
async getSchedulePresets(): Promise<Result<SchedulePresetsDto[], string>> { async getSchedulePresets(): Promise<Result<SchedulePresetsDto[], string>> {
try { try {
const presets = await this.prisma.schedulePresets.findMany({ const presets = await this.prisma.schedulePresets.findMany({
orderBy: [{ is_default: 'desc' }, { name: 'asc' }], orderBy: [{ name: 'asc' }],
include: { include: {
shifts: { shifts: {
orderBy: [{ week_day: 'asc' }, { start_time: 'asc' }], orderBy: [{ week_day: 'asc' }, { start_time: 'asc' }],
@ -28,7 +28,6 @@ export class SchedulePresetsGetService {
const response: SchedulePresetsDto[] = presets.map((preset) => ({ const response: SchedulePresetsDto[] = presets.map((preset) => ({
id: preset.id, id: preset.id,
name: preset.name, name: preset.name,
is_default: preset.is_default,
shifts: preset.shifts.map<Omit<SchedulePresetShiftsDto, 'id'>>((shift) => ({ shifts: preset.shifts.map<Omit<SchedulePresetShiftsDto, 'id'>>((shift) => ({
preset_id: shift.preset_id, preset_id: shift.preset_id,
week_day: shift.week_day, week_day: shift.week_day,

View File

@ -22,7 +22,6 @@ export class SchedulePresetUpdateService {
where: { id: dto.id }, where: { id: dto.id },
select: { select: {
id: true, id: true,
is_default: true,
shifts: true, shifts: true,
}, },
}); });
@ -52,22 +51,12 @@ export class SchedulePresetUpdateService {
} }
await this.prisma.$transaction(async (tx) => { await this.prisma.$transaction(async (tx) => {
if (dto.is_default) {
await tx.schedulePresets.updateMany({
where: {
is_default: true,
NOT: { id: existing.id },
},
data: { is_default: false },
});
}
await tx.schedulePresetShifts.deleteMany({ where: { preset_id: existing.id } }); await tx.schedulePresetShifts.deleteMany({ where: { preset_id: existing.id } });
await tx.schedulePresets.update({ await tx.schedulePresets.update({
where: { id: existing.id }, where: { id: existing.id },
data: { data: {
name: dto.name, name: dto.name,
is_default: dto.is_default ?? false,
shifts: { shifts: {
create: dto.shifts.map((shift, index) => { create: dto.shifts.map((shift, index) => {
const result = bank_code_results[index] as { success: true, data: number }; const result = bank_code_results[index] as { success: true, data: number };

View File

@ -14,7 +14,7 @@ export class TimesheetController {
) { } ) { }
@Get(':year/:period_number') @Get(':year/:period_number')
@ModuleAccessAllowed(ModulesEnum.timesheets_approval) @ModuleAccessAllowed(ModulesEnum.timesheets)
getTimesheetByPayPeriod( getTimesheetByPayPeriod(
@Access('email') email: string, @Access('email') email: string,
@Param('year', ParseIntPipe) year: number, @Param('year', ParseIntPipe) year: number,