refactor(modules): moved some module to change the structure to domain oriented

This commit is contained in:
Matthieu Haineault 2025-10-27 09:50:22 -04:00
parent 2712033451
commit 6c44bb5ad2
44 changed files with 739 additions and 1032 deletions

View File

@ -828,30 +828,6 @@
"Expense"
]
}
},
"/archives/expenses": {
"get": {
"operationId": "ExpensesArchiveController_findOneArchived",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"type": "number"
}
}
],
"responses": {
"200": {
"description": "Archived expense found"
}
},
"summary": "Fetch expense in archives with its Id",
"tags": [
"Expense Archives"
]
}
}
},
"info": {

View File

@ -2,17 +2,17 @@ import { BadRequestException, Module, ValidationPipe } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
// import { ArchivalModule } from './modules/archival/archival.module';
import { AuthenticationModule } from './modules/authentication/auth.module';
import { AuthenticationModule } from './identity-and-account/authentication/auth.module';
// import { BankCodesModule } from './modules/bank-codes/bank-codes.module';
// import { CsvExportModule } from './modules/exports/csv-exports.module';
import { HealthModule } from './health/health.module';
import { HealthController } from './health/health.controller';
import { NotificationsModule } from './modules/notifications/notifications.module';
import { OauthSessionsModule } from './modules/oauth-sessions/oauth-sessions.module';
import { PreferencesModule } from './modules/preferences/preferences.module';
import { OauthSessionsModule } from './identity-and-account/oauth-sessions/oauth-sessions.module';
import { PreferencesModule } from './identity-and-account/preferences/preferences.module';
import { PrismaModule } from './prisma/prisma.module';
import { ScheduleModule } from '@nestjs/schedule';
import { UsersModule } from './modules/users-management/users.module';
import { UsersModule } from './identity-and-account/users-management/users.module';
import { ConfigModule } from '@nestjs/config';
import { APP_FILTER, APP_PIPE } from '@nestjs/core';
import { HttpExceptionFilter } from './common/filters/http-exception.filter';

View File

@ -1,26 +1,26 @@
import { Controller, Get, Req, Res, UnauthorizedException, UseGuards } from '@nestjs/common';
import { OIDCLoginGuard } from '../guards/authentik-auth.guard';
import { Request, Response } from 'express';
@Controller('auth')
export class AuthController {
@UseGuards(OIDCLoginGuard)
@Get('/v1/login')
login() { }
@Get('/callback')
@UseGuards(OIDCLoginGuard)
loginCallback(@Req() req: Request, @Res() res: Response) {
res.redirect('http://localhost:9000/#/login-success');
}
@Get('/me')
getProfile(@Req() req: Request) {
if (!req.user) {
throw new UnauthorizedException('Not logged in');
}
return req.user;
}
}
import { Controller, Get, Req, Res, UnauthorizedException, UseGuards } from '@nestjs/common';
import { OIDCLoginGuard } from '../guards/authentik-auth.guard';
import { Request, Response } from 'express';
@Controller('auth')
export class AuthController {
@UseGuards(OIDCLoginGuard)
@Get('/v1/login')
login() { }
@Get('/callback')
@UseGuards(OIDCLoginGuard)
loginCallback(@Req() req: Request, @Res() res: Response) {
res.redirect('http://localhost:9000/#/login-success');
}
@Get('/me')
getProfile(@Req() req: Request) {
if (!req.user) {
throw new UnauthorizedException('Not logged in');
}
return req.user;
}
}

View File

@ -1,12 +1,12 @@
import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class OIDCLoginGuard extends AuthGuard('openidconnect') {
async canActivate(context: ExecutionContext) {
const result = (await super.canActivate(context)) as boolean;
const request = context.switchToHttp().getRequest();
await super.logIn(request);
return result;
}
import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class OIDCLoginGuard extends AuthGuard('openidconnect') {
async canActivate(context: ExecutionContext) {
const result = (await super.canActivate(context)) as boolean;
const request = context.switchToHttp().getRequest();
await super.logIn(request);
return result;
}
}

View File

@ -1,14 +1,14 @@
import { Injectable } from '@nestjs/common';
import { UsersService } from 'src/modules/users-management/services/users.service';
@Injectable()
export class AuthentikAuthService {
constructor(private usersService: UsersService) {}
async validateUser(user_email: string): Promise<any> {
const user = await this.usersService.findOneByEmail(user_email);
return user;
}
}
import { Injectable } from '@nestjs/common';
import { UsersService } from 'src/identity-and-account/users-management/services/users.service';
@Injectable()
export class AuthentikAuthService {
constructor(private usersService: UsersService) {}
async validateUser(user_email: string): Promise<any> {
const user = await this.usersService.findOneByEmail(user_email);
return user;
}
}

View File

@ -1,63 +1,63 @@
import { Strategy as OIDCStrategy, Profile, VerifyCallback } from 'passport-openidconnect';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { AuthentikAuthService } from '../services/authentik-auth.service';
import { ValidationError } from 'class-validator';
export interface AuthentikPayload {
iss: string; // Issuer
sub: string; // Subject (user ID)
aud: string; // Audience (client ID)
exp: number; // Expiration time (Unix)
iat: number; // Issued at time (Unix)
auth_time: number; // Time of authentication (Unix)
acr?: string; // Auth Context Class Reference
amr?: string[]; // Auth Method References (e.g., ['pwd'])
email: string;
email_verified: boolean;
name?: string;
given_name?: string;
preferred_username?: string;
nickname?: string;
groups?: string[];
}
@Injectable()
export class AuthentikStrategy extends PassportStrategy(OIDCStrategy, 'openidconnect', 8) {
constructor(private authentikAuthService: AuthentikAuthService) {
super({
issuer: process.env.AUTHENTIK_ISSUER || "ISSUER_MISSING",
clientID: process.env.AUTHENTIK_CLIENT_ID || 'CLIENT_ID_MISSING',
clientSecret: process.env.AUTHENTIK_CLIENT_SECRET || 'CLIENT_SECRET_MISSING',
callbackURL: process.env.AUTHENTIK_CALLBACK_URL || 'CALLBACK_URL_MISSING',
authorizationURL: process.env.AUTHENTIK_AUTH_URL || 'AUTH_URL_MISSING',
tokenURL: process.env.AUTHENTIK_TOKEN_URL || 'TOKEN_URL_MISSING',
userInfoURL: process.env.AUTHENTIK_USERINFO_URL || 'USERINFO_URL_MISSING',
scope: ['openid', 'email', 'profile', 'offline_access'],
},);
}
async validate(
_issuer: string,
profile: Profile,
_context: any,
_idToken: string,
_accessToken: string,
_refreshToken: string,
_params: any,
cb: VerifyCallback,
): Promise<any> {
try {
const email = profile.emails?.[0]?.value;
if (!email) return cb(new Error('Missing email in OIDC profile'), false);
const user = await this.authentikAuthService.validateUser(email);
if (!user) return cb(new Error('User not found'), false);
return cb(null, user);
} catch (err) {
return cb(err, false);
}
}
}
import { Strategy as OIDCStrategy, Profile, VerifyCallback } from 'passport-openidconnect';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { AuthentikAuthService } from '../services/authentik-auth.service';
import { ValidationError } from 'class-validator';
export interface AuthentikPayload {
iss: string; // Issuer
sub: string; // Subject (user ID)
aud: string; // Audience (client ID)
exp: number; // Expiration time (Unix)
iat: number; // Issued at time (Unix)
auth_time: number; // Time of authentication (Unix)
acr?: string; // Auth Context Class Reference
amr?: string[]; // Auth Method References (e.g., ['pwd'])
email: string;
email_verified: boolean;
name?: string;
given_name?: string;
preferred_username?: string;
nickname?: string;
groups?: string[];
}
@Injectable()
export class AuthentikStrategy extends PassportStrategy(OIDCStrategy, 'openidconnect', 8) {
constructor(private authentikAuthService: AuthentikAuthService) {
super({
issuer: process.env.AUTHENTIK_ISSUER || "ISSUER_MISSING",
clientID: process.env.AUTHENTIK_CLIENT_ID || 'CLIENT_ID_MISSING',
clientSecret: process.env.AUTHENTIK_CLIENT_SECRET || 'CLIENT_SECRET_MISSING',
callbackURL: process.env.AUTHENTIK_CALLBACK_URL || 'CALLBACK_URL_MISSING',
authorizationURL: process.env.AUTHENTIK_AUTH_URL || 'AUTH_URL_MISSING',
tokenURL: process.env.AUTHENTIK_TOKEN_URL || 'TOKEN_URL_MISSING',
userInfoURL: process.env.AUTHENTIK_USERINFO_URL || 'USERINFO_URL_MISSING',
scope: ['openid', 'email', 'profile', 'offline_access'],
},);
}
async validate(
_issuer: string,
profile: Profile,
_context: any,
_idToken: string,
_accessToken: string,
_refreshToken: string,
_params: any,
cb: VerifyCallback,
): Promise<any> {
try {
const email = profile.emails?.[0]?.value;
if (!email) return cb(new Error('Missing email in OIDC profile'), false);
const user = await this.authentikAuthService.validateUser(email);
if (!user) return cb(new Error('User not found'), false);
return cb(null, user);
} catch (err) {
return cb(err, false);
}
}
}

View File

@ -1,13 +1,12 @@
// import { Module } from '@nestjs/common';
// import { EmployeesController } from './controllers/employees.controller';
// import { EmployeesService } from './services/employees.service';
// import { EmployeesArchivalService } from './services/employees-archival.service';
// import { SharedModule } from '../../time-and-attendance/modules/shared/shared.module';
// @Module({
// imports: [SharedModule],
// controllers: [EmployeesController],
// providers: [EmployeesService, EmployeesArchivalService],
// exports: [EmployeesService, EmployeesArchivalService],
// providers: [EmployeesService],
// exports: [EmployeesService ],
// })
// export class EmployeesModule {}

View File

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

View File

@ -1,10 +0,0 @@
// import { Module } from '@nestjs/common';
// import { CustomersController } from './controllers/customers.controller';
// import { CustomersService } from './services/customers.service';
// @Module({
// controllers:[CustomersController],
// providers:[CustomersService],
// })
// export class CustomersModule {}

View File

@ -1,79 +0,0 @@
// import { ApiProperty } from "@nestjs/swagger";
// import { Type } from "class-transformer";
// import {
// Allow,
// IsEmail,
// IsInt,
// IsNotEmpty,
// IsOptional,
// IsPositive,
// IsString,
// IsUUID,
// } from "class-validator";
// export class CreateCustomerDto {
// @ApiProperty({
// example: 1,
// description: 'Unique ID of a customer(primary-key, auto-incremented)',
// })
// @Allow()
// id?: number;
// @ApiProperty({
// example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d',
// description: 'UUID of the user linked to that customer',
// })
// @IsUUID()
// @IsOptional()
// user_id?: string;
// @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',
// })
// @IsString()
// phone_number: string;
// @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,
// })
// @Type(() => Number)
// @IsInt()
// @IsNotEmpty()
// invoice_id: number;
// }

View File

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

View File

@ -1,93 +0,0 @@
// import { Injectable } from '@nestjs/common';
// @Injectable()
// export class CustomersService {
// //_____________________________________________________________________________________________
// // Deprecated or unused methods
// //_____________________________________________________________________________________________
// // constructor(private readonly prisma: PrismaService) {}
// // async create(dto: CreateCustomerDto): Promise<Customers> {
// // const {
// // first_name,
// // last_name,
// // email,
// // phone_number,
// // residence,
// // invoice_id,
// // } = dto;
// // return this.prisma.$transaction(async (transaction) => {
// // const user: Users = await transaction.users.create({
// // data: {
// // first_name,
// // last_name,
// // email,
// // phone_number,
// // residence,
// // },
// // });
// // return transaction.customers.create({
// // data: {
// // user_id: user.id,
// // invoice_id,
// // },
// // });
// // });
// // }
// // findAll(): Promise<Customers[]> {
// // return this.prisma.customers.findMany({
// // include: { user: true },
// // })
// // }
// // async findOne(id:number): Promise<Customers> {
// // const customer = await this.prisma.customers.findUnique({
// // where: { id },
// // include: { user: true },
// // });
// // if(!customer) throw new NotFoundException(`Customer #${id} not found`);
// // return customer;
// // }
// // async update(id: number,dto: UpdateCustomerDto): Promise<Customers> {
// // const customer = await this.findOne(id);
// // const {
// // first_name,
// // last_name,
// // email,
// // phone_number,
// // residence,
// // invoice_id,
// // } = dto;
// // return this.prisma.$transaction(async (transaction) => {
// // await transaction.users.update({
// // where: { id: customer.user_id },
// // data: {
// // ...(first_name !== undefined && { first_name }),
// // ...(last_name !== undefined && { last_name }),
// // ...(email !== undefined && { email }),
// // ...(phone_number !== undefined && { phone_number }),
// // ...(residence !== undefined && { residence }),
// // },
// // });
// // return transaction.customers.update({
// // where: { id },
// // data: {
// // ...(invoice_id !== undefined && { invoice_id }),
// // },
// // });
// // });
// // }
// // async remove(id: number): Promise<Customers> {
// // await this.findOne(id);
// // return this.prisma.customers.delete({ where: { id }});
// // }
// }

View File

@ -1,12 +0,0 @@
// import { Module } from '@nestjs/common';
// import { EmployeesController } from './controllers/employees.controller';
// import { EmployeesService } from './services/employees.service';
// import { SharedModule } from '../../time-and-attendance/modules/shared/shared.module';
// @Module({
// imports: [SharedModule],
// controllers: [EmployeesController],
// providers: [EmployeesService],
// exports: [EmployeesService ],
// })
// export class EmployeesModule {}

View File

@ -1,30 +1,30 @@
// import { Body, Controller, Post } from "@nestjs/common";
// import { ApiBearerAuth, ApiTags } from "@nestjs/swagger";
// import { LeaveRequestsService } from "../services/leave-request.service";
// import { UpsertLeaveRequestDto } from "../dtos/upsert-leave-request.dto";
// import { LeaveTypes } from "@prisma/client";
import { Body, Controller, Post } from "@nestjs/common";
import { ApiBearerAuth, ApiTags } from "@nestjs/swagger";
import { LeaveRequestsService } from "../services/leave-request.service";
import { UpsertLeaveRequestDto } from "../dtos/upsert-leave-request.dto";
import { LeaveTypes } from "@prisma/client";
// @ApiTags('Leave Requests')
// @ApiBearerAuth('access-token')
// // @UseGuards()
// @Controller('leave-requests')
// export class LeaveRequestController {
// constructor(private readonly leave_service: LeaveRequestsService){}
@ApiTags('Leave Requests')
@ApiBearerAuth('access-token')
// @UseGuards()
@Controller('leave-requests')
export class LeaveRequestController {
constructor(private readonly leave_service: LeaveRequestsService){}
// @Post('upsert')
// async upsertLeaveRequest(@Body() dto: UpsertLeaveRequestDto) {
// const { action, leave_requests } = await this.leave_service.handle(dto);
// return { action, leave_requests };
// }q
@Post('upsert')
async upsertLeaveRequest(@Body() dto: UpsertLeaveRequestDto) {
const { action, leave_requests } = await this.leave_service.handle(dto);
return { action, leave_requests };
}q
// //TODO:
// /*
// @Get('archive')
// findAllArchived(){...}
//TODO:
/*
@Get('archive')
findAllArchived(){...}
// @Get('archive/:id')
// findOneArchived(id){...}
// */
@Get('archive/:id')
findOneArchived(id){...}
*/
// }
}

View File

@ -1,29 +1,30 @@
// import { PrismaService } from "src/prisma/prisma.service";
// import { LeaveRequestController } from "./controllers/leave-requests.controller";
// import { HolidayLeaveRequestsService } from "./services/holiday-leave-requests.service";
// import { Module } from "@nestjs/common";
// import { BusinessLogicsModule } from "src/modules/business-logics/business-logics.module";
// import { VacationLeaveRequestsService } from "./services/vacation-leave-requests.service";
// import { SickLeaveRequestsService } from "./services/sick-leave-requests.service";
// import { LeaveRequestsService } from "./services/leave-request.service";
// import { ShiftsModule } from "../shifts/shifts.module";
// import { LeaveRequestsUtils } from "./utils/leave-request.util";
// import { SharedModule } from "../shared/shared.module";
import { Module } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service";
import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module";
import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service";
import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
import { LeaveRequestController } from "src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller";
import { LeaveRequestsService } from "src/time-and-attendance/modules/leave-requests/services/leave-request.service";
import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util";
import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module";
import { ShiftsModule } from "src/time-and-attendance/modules/time-tracker/shifts/shifts.module";
// @Module({
// imports: [BusinessLogicsModule, ShiftsModule, SharedModule],
// controllers: [LeaveRequestController],
// providers: [
// VacationLeaveRequestsService,
// SickLeaveRequestsService,
// HolidayLeaveRequestsService,
// LeaveRequestsService,
// PrismaService,
// LeaveRequestsUtils,
// ],
// exports: [
// LeaveRequestsService,
// ],
// })
// export class LeaveRequestsModule {}
@Module({
imports: [BusinessLogicsModule, ShiftsModule, SharedModule],
controllers: [LeaveRequestController],
providers: [
VacationService,
SickLeaveService,
HolidayService,
LeaveRequestsService,
PrismaService,
LeaveRequestsUtils,
],
exports: [
LeaveRequestsService,
],
})
export class LeaveRequestsModule {}

View File

@ -1,78 +1,78 @@
// import { UpsertLeaveRequestDto, UpsertResult } from '../dtos/upsert-leave-request.dto';
// import { LeaveRequestViewDto } from '../dtos/leave-request-view.dto';
// import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
// import { LeaveApprovalStatus, LeaveTypes } from '@prisma/client';
// import { HolidayService } from 'src/modules/business-logics/services/holiday.service';
// import { PrismaService } from 'src/prisma/prisma.service';
// import { mapRowToView } from '../mappers/leave-requests.mapper';
// import { leaveRequestsSelect } from '../utils/leave-requests.select';
// import { LeaveRequestsUtils} from '../utils/leave-request.util';
// import { normalizeDates, toDateOnly } from 'src/modules/shared/helpers/date-time.helpers';
// import { BankCodesResolver } from 'src/modules/shared/utils/resolve-bank-type-id.utils';
// import { EmailToIdResolver } from 'src/modules/shared/utils/resolve-email-id.utils';
import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common";
import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client";
import { PrismaService } from "src/prisma/prisma.service";
import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service";
import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto";
import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto";
import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper";
import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util";
import { leaveRequestsSelect } from "src/time-and-attendance/modules/leave-requests/utils/leave-requests.select";
import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers";
import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils";
import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils";
// @Injectable()
// export class HolidayLeaveRequestsService {
// constructor(
// private readonly prisma: PrismaService,
// private readonly holidayService: HolidayService,
// private readonly leaveUtils: LeaveRequestsUtils,
// private readonly emailResolver: EmailToIdResolver,
// private readonly typeResolver: BankCodesResolver,
// ) {}
@Injectable()
export class HolidayLeaveRequestsService {
constructor(
private readonly prisma: PrismaService,
private readonly holidayService: HolidayService,
private readonly leaveUtils: LeaveRequestsUtils,
private readonly emailResolver: EmailToIdResolver,
private readonly typeResolver: BankCodesResolver,
) {}
// async create(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
// const email = dto.email.trim();
// const employee_id = await this.emailResolver.findIdByEmail(email);
// const bank_code = await this.typeResolver.findByType(LeaveTypes.HOLIDAY);
// if(!bank_code) throw new NotFoundException(`bank_code not found`);
// const dates = normalizeDates(dto.dates);
// if (!dates.length) throw new BadRequestException('Dates array must not be empty');
async create(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
const email = dto.email.trim();
const employee_id = await this.emailResolver.findIdByEmail(email);
const bank_code = await this.typeResolver.findByType(LeaveTypes.HOLIDAY);
if(!bank_code) throw new NotFoundException(`bank_code not found`);
const dates = normalizeDates(dto.dates);
if (!dates.length) throw new BadRequestException('Dates array must not be empty');
// const created: LeaveRequestViewDto[] = [];
const created: LeaveRequestViewDto[] = [];
// for (const iso_date of dates) {
// const date = toDateOnly(iso_date);
for (const iso_date of dates) {
const date = toDateOnly(iso_date);
// const existing = await this.prisma.leaveRequests.findUnique({
// where: {
// leave_per_employee_date: {
// employee_id: employee_id,
// leave_type: LeaveTypes.HOLIDAY,
// date,
// },
// },
// select: { id: true },
// });
// if (existing) {
// throw new BadRequestException(`Holiday request already exists for ${iso_date}`);
// }
const existing = await this.prisma.leaveRequests.findUnique({
where: {
leave_per_employee_date: {
employee_id: employee_id,
leave_type: LeaveTypes.HOLIDAY,
date,
},
},
select: { id: true },
});
if (existing) {
throw new BadRequestException(`Holiday request already exists for ${iso_date}`);
}
// const payable = await this.holidayService.calculateHolidayPay(email, date, bank_code.modifier);
// const row = await this.prisma.leaveRequests.create({
// data: {
// employee_id: employee_id,
// bank_code_id: bank_code.id,
// leave_type: LeaveTypes.HOLIDAY,
// date,
// comment: dto.comment ?? '',
// requested_hours: dto.requested_hours ?? 8,
// payable_hours: payable,
// approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING,
// },
// select: leaveRequestsSelect,
// });
const payable = await this.holidayService.calculateHolidayPay(email, date, bank_code.modifier);
const row = await this.prisma.leaveRequests.create({
data: {
employee_id: employee_id,
bank_code_id: bank_code.id,
leave_type: LeaveTypes.HOLIDAY,
date,
comment: dto.comment ?? '',
requested_hours: dto.requested_hours ?? 8,
payable_hours: payable,
approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING,
},
select: leaveRequestsSelect,
});
// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
// if (row.approval_status === LeaveApprovalStatus.APPROVED) {
// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours,LeaveTypes.HOLIDAY, row.comment);
// }
const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
if (row.approval_status === LeaveApprovalStatus.APPROVED) {
await this.leaveUtils.syncShift(email, employee_id, iso_date, hours,LeaveTypes.HOLIDAY, row.comment);
}
// created.push({ ...mapRowToView(row), action: 'create' });
// }
created.push({ ...mapRowToView(row), action: 'create' });
}
// return { action: 'create', leave_requests: created };
// }
// }
return { action: 'create', leave_requests: created };
}
}

View File

@ -1,248 +1,243 @@
// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
// import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client";
// import { roundToQuarterHour } from "src/common/utils/date-utils";
// import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto";
// import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto";
// import { mapRowToView } from "../mappers/leave-requests.mapper";
// import { leaveRequestsSelect } from "../utils/leave-requests.select";
// import { HolidayLeaveRequestsService } from "./holiday-leave-requests.service";
// import { SickLeaveRequestsService } from "./sick-leave-requests.service";
// import { VacationLeaveRequestsService } from "./vacation-leave-requests.service";
// import { HolidayService } from "src/modules/business-logics/services/holiday.service";
// import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service";
// import { VacationService } from "src/modules/business-logics/services/vacation.service";
// import { PrismaService } from "src/prisma/prisma.service";
// import { LeaveRequestsUtils } from "../utils/leave-request.util";
// import { normalizeDates, toDateOnly, toISODateKey } from "src/modules/shared/helpers/date-time.helpers";
// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils";
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client";
import { roundToQuarterHour } from "src/common/utils/date-utils";
import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto";
import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto";
import { mapRowToView } from "../mappers/leave-requests.mapper";
import { leaveRequestsSelect } from "../utils/leave-requests.select";
import { PrismaService } from "src/prisma/prisma.service";
import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service";
import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
import { normalizeDates, toDateOnly, toISODateKey } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers";
import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils";
import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils";
import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util";
@Injectable()
export class LeaveRequestsService {
constructor(
private readonly prisma: PrismaService,
private readonly holidayService: HolidayService,
private readonly sickLogic: SickLeaveService,
private readonly sickLeaveService: SickLeaveService,
private readonly vacationService: VacationService,
private readonly vacationLogic: VacationService,
private readonly leaveUtils: LeaveRequestsUtils,
private readonly emailResolver: EmailToIdResolver,
private readonly typeResolver: BankCodesResolver,
) {}
// @Injectable()
// export class LeaveRequestsService {
// constructor(
// private readonly prisma: PrismaService,
// private readonly holidayLeaveService: HolidayLeaveRequestsService,
// private readonly holidayService: HolidayService,
// private readonly sickLogic: SickLeaveService,
// private readonly sickLeaveService: SickLeaveRequestsService,
// private readonly vacationLeaveService: VacationLeaveRequestsService,
// private readonly vacationLogic: VacationService,
// private readonly leaveUtils: LeaveRequestsUtils,
// private readonly emailResolver: EmailToIdResolver,
// private readonly typeResolver: BankCodesResolver,
// ) {}
// handle distribution to the right service according to the selected type and action
async handle(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
switch (dto.type) {
case LeaveTypes.HOLIDAY:
if( dto.action === 'create'){
// return this.holidayService.create(dto);
} else if (dto.action === 'update') {
return this.update(dto, LeaveTypes.HOLIDAY);
} else if (dto.action === 'delete'){
return this.delete(dto, LeaveTypes.HOLIDAY);
}
case LeaveTypes.VACATION:
if( dto.action === 'create'){
// return this.vacationService.create(dto);
} else if (dto.action === 'update') {
return this.update(dto, LeaveTypes.VACATION);
} else if (dto.action === 'delete'){
return this.delete(dto, LeaveTypes.VACATION);
}
case LeaveTypes.SICK:
if( dto.action === 'create'){
// return this.sickLeaveService.create(dto);
} else if (dto.action === 'update') {
return this.update(dto, LeaveTypes.SICK);
} else if (dto.action === 'delete'){
return this.delete(dto, LeaveTypes.SICK);
}
default:
throw new BadRequestException(`Unsupported leave type: ${dto.type} or action: ${dto.action}`);
}
}
// //handle distribution to the right service according to the selected type and action
// async handle(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
// switch (dto.type) {
// case LeaveTypes.HOLIDAY:
// if( dto.action === 'create'){
// return this.holidayLeaveService.create(dto);
// } else if (dto.action === 'update') {
// return this.update(dto, LeaveTypes.HOLIDAY);
// } else if (dto.action === 'delete'){
// return this.delete(dto, LeaveTypes.HOLIDAY);
// }
// case LeaveTypes.VACATION:
// if( dto.action === 'create'){
// return this.vacationLeaveService.create(dto);
// } else if (dto.action === 'update') {
// return this.update(dto, LeaveTypes.VACATION);
// } else if (dto.action === 'delete'){
// return this.delete(dto, LeaveTypes.VACATION);
// }
// case LeaveTypes.SICK:
// if( dto.action === 'create'){
// return this.sickLeaveService.create(dto);
// } else if (dto.action === 'update') {
// return this.update(dto, LeaveTypes.SICK);
// } else if (dto.action === 'delete'){
// return this.delete(dto, LeaveTypes.SICK);
// }
// default:
// throw new BadRequestException(`Unsupported leave type: ${dto.type} or action: ${dto.action}`);
// }
// }
async delete(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise<UpsertResult> {
const email = dto.email.trim();
const dates = normalizeDates(dto.dates);
const employee_id = await this.emailResolver.findIdByEmail(email);
if (!dates.length) throw new BadRequestException("Dates array must not be empty");
// async delete(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise<UpsertResult> {
// const email = dto.email.trim();
// const dates = normalizeDates(dto.dates);
// const employee_id = await this.emailResolver.findIdByEmail(email);
// if (!dates.length) throw new BadRequestException("Dates array must not be empty");
const rows = await this.prisma.leaveRequests.findMany({
where: {
employee_id: employee_id,
leave_type: type,
date: { in: dates.map((d) => toDateOnly(d)) },
},
select: leaveRequestsSelect,
});
// const rows = await this.prisma.leaveRequests.findMany({
// where: {
// employee_id: employee_id,
// leave_type: type,
// date: { in: dates.map((d) => toDateOnly(d)) },
// },
// select: leaveRequestsSelect,
// });
if (rows.length !== dates.length) {
const missing = dates.filter((isoDate) => !rows.some((row) => toISODateKey(row.date) === isoDate));
throw new NotFoundException(`No Leave request found for: ${missing.join(", ")}`);
}
// if (rows.length !== dates.length) {
// const missing = dates.filter((isoDate) => !rows.some((row) => toISODateKey(row.date) === isoDate));
// throw new NotFoundException(`No Leave request found for: ${missing.join(", ")}`);
// }
for (const row of rows) {
if (row.approval_status === LeaveApprovalStatus.APPROVED) {
const iso = toISODateKey(row.date);
await this.leaveUtils.removeShift(email, employee_id, iso, type);
}
}
// for (const row of rows) {
// if (row.approval_status === LeaveApprovalStatus.APPROVED) {
// const iso = toISODateKey(row.date);
// await this.leaveUtils.removeShift(email, employee_id, iso, type);
// }
// }
await this.prisma.leaveRequests.deleteMany({
where: { id: { in: rows.map((row) => row.id) } },
});
// await this.prisma.leaveRequests.deleteMany({
// where: { id: { in: rows.map((row) => row.id) } },
// });
const deleted = rows.map((row) => ({ ...mapRowToView(row), action: "delete" as const }));
return { action: "delete", leave_requests: deleted };
}
// const deleted = rows.map((row) => ({ ...mapRowToView(row), action: "delete" as const }));
// return { action: "delete", leave_requests: deleted };
// }
async update(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise<UpsertResult> {
const email = dto.email.trim();
const employee_id = await this.emailResolver.findIdByEmail(email);
const bank_code = await this.typeResolver.findByType(type);
if(!bank_code) throw new NotFoundException(`bank_code not found`);
const modifier = Number(bank_code.modifier ?? 1);
const dates = normalizeDates(dto.dates);
if (!dates.length) {
throw new BadRequestException("Dates array must not be empty");
}
// async update(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise<UpsertResult> {
// const email = dto.email.trim();
// const employee_id = await this.emailResolver.findIdByEmail(email);
// const bank_code = await this.typeResolver.findByType(type);
// if(!bank_code) throw new NotFoundException(`bank_code not found`);
// const modifier = Number(bank_code.modifier ?? 1);
// const dates = normalizeDates(dto.dates);
// if (!dates.length) {
// throw new BadRequestException("Dates array must not be empty");
// }
const entries = await Promise.all(
dates.map(async (iso_date) => {
const date = toDateOnly(iso_date);
const existing = await this.prisma.leaveRequests.findUnique({
where: {
leave_per_employee_date: {
employee_id: employee_id,
leave_type: type,
date,
},
},
select: leaveRequestsSelect,
});
if (!existing) throw new NotFoundException(`No Leave request found for ${iso_date}`);
return { iso_date, date, existing };
}),
);
// const entries = await Promise.all(
// dates.map(async (iso_date) => {
// const date = toDateOnly(iso_date);
// const existing = await this.prisma.leaveRequests.findUnique({
// where: {
// leave_per_employee_date: {
// employee_id: employee_id,
// leave_type: type,
// date,
// },
// },
// select: leaveRequestsSelect,
// });
// if (!existing) throw new NotFoundException(`No Leave request found for ${iso_date}`);
// return { iso_date, date, existing };
// }),
// );
const updated: LeaveRequestViewDto[] = [];
// const updated: LeaveRequestViewDto[] = [];
if (type === LeaveTypes.SICK) {
const firstExisting = entries[0].existing;
const fallbackRequested =
firstExisting.requested_hours !== null && firstExisting.requested_hours !== undefined
? Number(firstExisting.requested_hours)
: 8;
const requested_hours_per_day = dto.requested_hours ?? fallbackRequested;
const reference_date = entries.reduce(
(latest, entry) => (entry.date > latest ? entry.date : latest),
entries[0].date,
);
const total_payable_hours = await this.sickLogic.calculateSickLeavePay(
employee_id,
reference_date,
entries.length,
requested_hours_per_day,
modifier,
);
let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours));
const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier);
// if (type === LeaveTypes.SICK) {
// const firstExisting = entries[0].existing;
// const fallbackRequested =
// firstExisting.requested_hours !== null && firstExisting.requested_hours !== undefined
// ? Number(firstExisting.requested_hours)
// : 8;
// const requested_hours_per_day = dto.requested_hours ?? fallbackRequested;
// const reference_date = entries.reduce(
// (latest, entry) => (entry.date > latest ? entry.date : latest),
// entries[0].date,
// );
// const total_payable_hours = await this.sickLogic.calculateSickLeavePay(
// employee_id,
// reference_date,
// entries.length,
// requested_hours_per_day,
// modifier,
// );
// let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours));
// const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier);
for (const { iso_date, existing } of entries) {
const previous_status = existing.approval_status;
const payable = Math.min(remaining_payable_hours, daily_payable_cap);
const payable_rounded = roundToQuarterHour(Math.max(0, payable));
remaining_payable_hours = roundToQuarterHour(
Math.max(0, remaining_payable_hours - payable_rounded),
);
// for (const { iso_date, existing } of entries) {
// const previous_status = existing.approval_status;
// const payable = Math.min(remaining_payable_hours, daily_payable_cap);
// const payable_rounded = roundToQuarterHour(Math.max(0, payable));
// remaining_payable_hours = roundToQuarterHour(
// Math.max(0, remaining_payable_hours - payable_rounded),
// );
const row = await this.prisma.leaveRequests.update({
where: { id: existing.id },
data: {
comment: dto.comment ?? existing.comment,
requested_hours: requested_hours_per_day,
payable_hours: payable_rounded,
bank_code_id: bank_code.id,
approval_status: dto.approval_status ?? existing.approval_status,
},
select: leaveRequestsSelect,
});
// const row = await this.prisma.leaveRequests.update({
// where: { id: existing.id },
// data: {
// comment: dto.comment ?? existing.comment,
// requested_hours: requested_hours_per_day,
// payable_hours: payable_rounded,
// bank_code_id: bank_code.id,
// approval_status: dto.approval_status ?? existing.approval_status,
// },
// select: leaveRequestsSelect,
// });
const was_approved = previous_status === LeaveApprovalStatus.APPROVED;
const is_approved = row.approval_status === LeaveApprovalStatus.APPROVED;
const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
// const was_approved = previous_status === LeaveApprovalStatus.APPROVED;
// const is_approved = row.approval_status === LeaveApprovalStatus.APPROVED;
// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
if (!was_approved && is_approved) {
await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
} else if (was_approved && !is_approved) {
await this.leaveUtils.removeShift(email, employee_id, iso_date, type);
} else if (was_approved && is_approved) {
await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
}
updated.push({ ...mapRowToView(row), action: "update" });
}
return { action: "update", leave_requests: updated };
}
// if (!was_approved && is_approved) {
// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
// } else if (was_approved && !is_approved) {
// await this.leaveUtils.removeShift(email, employee_id, iso_date, type);
// } else if (was_approved && is_approved) {
// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
// }
// updated.push({ ...mapRowToView(row), action: "update" });
// }
// return { action: "update", leave_requests: updated };
// }
for (const { iso_date, date, existing } of entries) {
const previous_status = existing.approval_status;
const fallbackRequested =
existing.requested_hours !== null && existing.requested_hours !== undefined
? Number(existing.requested_hours)
: 8;
const requested_hours = dto.requested_hours ?? fallbackRequested;
// for (const { iso_date, date, existing } of entries) {
// const previous_status = existing.approval_status;
// const fallbackRequested =
// existing.requested_hours !== null && existing.requested_hours !== undefined
// ? Number(existing.requested_hours)
// : 8;
// const requested_hours = dto.requested_hours ?? fallbackRequested;
let payable: number;
switch (type) {
case LeaveTypes.HOLIDAY:
payable = await this.holidayService.calculateHolidayPay(email, date, modifier);
break;
case LeaveTypes.VACATION: {
const days_requested = requested_hours / 8;
payable = await this.vacationLogic.calculateVacationPay(
employee_id,
date,
Math.max(0, days_requested),
modifier,
);
break;
}
default:
payable = existing.payable_hours !== null && existing.payable_hours !== undefined
? Number(existing.payable_hours)
: requested_hours;
}
// let payable: number;
// switch (type) {
// case LeaveTypes.HOLIDAY:
// payable = await this.holidayService.calculateHolidayPay(email, date, modifier);
// break;
// case LeaveTypes.VACATION: {
// const days_requested = requested_hours / 8;
// payable = await this.vacationLogic.calculateVacationPay(
// employee_id,
// date,
// Math.max(0, days_requested),
// modifier,
// );
// break;
// }
// default:
// payable = existing.payable_hours !== null && existing.payable_hours !== undefined
// ? Number(existing.payable_hours)
// : requested_hours;
// }
const row = await this.prisma.leaveRequests.update({
where: { id: existing.id },
data: {
requested_hours,
comment: dto.comment ?? existing.comment,
payable_hours: payable,
bank_code_id: bank_code.id,
approval_status: dto.approval_status ?? existing.approval_status,
},
select: leaveRequestsSelect,
});
// const row = await this.prisma.leaveRequests.update({
// where: { id: existing.id },
// data: {
// requested_hours,
// comment: dto.comment ?? existing.comment,
// payable_hours: payable,
// bank_code_id: bank_code.id,
// approval_status: dto.approval_status ?? existing.approval_status,
// },
// select: leaveRequestsSelect,
// });
const was_approved = previous_status === LeaveApprovalStatus.APPROVED;
const is_approved = row.approval_status === LeaveApprovalStatus.APPROVED;
const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
// const was_approved = previous_status === LeaveApprovalStatus.APPROVED;
// const is_approved = row.approval_status === LeaveApprovalStatus.APPROVED;
// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
// if (!was_approved && is_approved) {
// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
// } else if (was_approved && !is_approved) {
// await this.leaveUtils.removeShift(email, employee_id, iso_date, type);
// } else if (was_approved && is_approved) {
// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
// }
// updated.push({ ...mapRowToView(row), action: "update" });
// }
// return { action: "update", leave_requests: updated };
// }
// }
if (!was_approved && is_approved) {
await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
} else if (was_approved && !is_approved) {
await this.leaveUtils.removeShift(email, employee_id, iso_date, type);
} else if (was_approved && is_approved) {
await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
}
updated.push({ ...mapRowToView(row), action: "update" });
}
return { action: "update", leave_requests: updated };
}
}

View File

@ -1,98 +1,99 @@
// import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto";
// import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto";
// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
// import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client";
// import { leaveRequestsSelect } from "../utils/leave-requests.select";
// import { mapRowToView } from "../mappers/leave-requests.mapper";
// import { PrismaService } from "src/prisma/prisma.service";
// import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service";
// import { roundToQuarterHour } from "src/common/utils/date-utils";
// import { LeaveRequestsUtils } from "../utils/leave-request.util";
// import { normalizeDates, toDateOnly } from "src/modules/shared/helpers/date-time.helpers";
// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils";
import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common";
import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client";
import { roundToQuarterHour } from "src/common/utils/date-utils";
import { PrismaService } from "src/prisma/prisma.service";
import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto";
import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto";
import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper";
import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util";
import { leaveRequestsSelect } from "src/time-and-attendance/modules/leave-requests/utils/leave-requests.select";
import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers";
import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils";
import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils";
// @Injectable()
// export class SickLeaveRequestsService {
// constructor(
// private readonly prisma: PrismaService,
// private readonly sickService: SickLeaveService,
// private readonly leaveUtils: LeaveRequestsUtils,
// private readonly emailResolver: EmailToIdResolver,
// private readonly typeResolver: BankCodesResolver,
// ) {}
// async create(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
// const email = dto.email.trim();
// const employee_id = await this.emailResolver.findIdByEmail(email);
// const bank_code = await this.typeResolver.findByType(LeaveTypes.SICK);
// if(!bank_code) throw new NotFoundException(`bank_code not found`);
@Injectable()
export class SickLeaveRequestsService {
constructor(
private readonly prisma: PrismaService,
private readonly sickService: SickLeaveService,
private readonly leaveUtils: LeaveRequestsUtils,
private readonly emailResolver: EmailToIdResolver,
private readonly typeResolver: BankCodesResolver,
) {}
async create(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
const email = dto.email.trim();
const employee_id = await this.emailResolver.findIdByEmail(email);
const bank_code = await this.typeResolver.findByType(LeaveTypes.SICK);
if(!bank_code) throw new NotFoundException(`bank_code not found`);
// const modifier = bank_code.modifier ?? 1;
// const dates = normalizeDates(dto.dates);
// if (!dates.length) throw new BadRequestException("Dates array must not be empty");
// const requested_hours_per_day = dto.requested_hours ?? 8;
const modifier = bank_code.modifier ?? 1;
const dates = normalizeDates(dto.dates);
if (!dates.length) throw new BadRequestException("Dates array must not be empty");
const requested_hours_per_day = dto.requested_hours ?? 8;
// const entries = dates.map((iso) => ({ iso, date: toDateOnly(iso) }));
// const reference_date = entries.reduce(
// (latest, entry) => (entry.date > latest ? entry.date : latest),
// entries[0].date,
// );
// const total_payable_hours = await this.sickService.calculateSickLeavePay(
// employee_id,
// reference_date,
// entries.length,
// requested_hours_per_day,
// modifier,
// );
// let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours));
// const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier);
const entries = dates.map((iso) => ({ iso, date: toDateOnly(iso) }));
const reference_date = entries.reduce(
(latest, entry) => (entry.date > latest ? entry.date : latest),
entries[0].date,
);
const total_payable_hours = await this.sickService.calculateSickLeavePay(
employee_id,
reference_date,
entries.length,
requested_hours_per_day,
modifier,
);
let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours));
const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier);
// const created: LeaveRequestViewDto[] = [];
const created: LeaveRequestViewDto[] = [];
// for (const { iso, date } of entries) {
// const existing = await this.prisma.leaveRequests.findUnique({
// where: {
// leave_per_employee_date: {
// employee_id: employee_id,
// leave_type: LeaveTypes.SICK,
// date,
// },
// },
// select: { id: true },
// });
// if (existing) {
// throw new BadRequestException(`Sick request already exists for ${iso}`);
// }
for (const { iso, date } of entries) {
const existing = await this.prisma.leaveRequests.findUnique({
where: {
leave_per_employee_date: {
employee_id: employee_id,
leave_type: LeaveTypes.SICK,
date,
},
},
select: { id: true },
});
if (existing) {
throw new BadRequestException(`Sick request already exists for ${iso}`);
}
// const payable = Math.min(remaining_payable_hours, daily_payable_cap);
// const payable_rounded = roundToQuarterHour(Math.max(0, payable));
// remaining_payable_hours = roundToQuarterHour(
// Math.max(0, remaining_payable_hours - payable_rounded),
// );
const payable = Math.min(remaining_payable_hours, daily_payable_cap);
const payable_rounded = roundToQuarterHour(Math.max(0, payable));
remaining_payable_hours = roundToQuarterHour(
Math.max(0, remaining_payable_hours - payable_rounded),
);
// const row = await this.prisma.leaveRequests.create({
// data: {
// employee_id: employee_id,
// bank_code_id: bank_code.id,
// leave_type: LeaveTypes.SICK,
// comment: dto.comment ?? "",
// requested_hours: requested_hours_per_day,
// payable_hours: payable_rounded,
// approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING,
// date,
// },
// select: leaveRequestsSelect,
// });
const row = await this.prisma.leaveRequests.create({
data: {
employee_id: employee_id,
bank_code_id: bank_code.id,
leave_type: LeaveTypes.SICK,
comment: dto.comment ?? "",
requested_hours: requested_hours_per_day,
payable_hours: payable_rounded,
approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING,
date,
},
select: leaveRequestsSelect,
});
// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
// if (row.approval_status === LeaveApprovalStatus.APPROVED) {
// await this.leaveUtils.syncShift(email, employee_id, iso, hours,LeaveTypes.SICK, row.comment);
// }
const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
if (row.approval_status === LeaveApprovalStatus.APPROVED) {
// await this.leaveUtils.syncShift(email, employee_id, iso, hours,LeaveTypes.SICK, row.comment);
}
// created.push({ ...mapRowToView(row), action: "create" });
// }
created.push({ ...mapRowToView(row), action: "create" });
}
// return { action: "create", leave_requests: created };
// }
// }
return { action: "create", leave_requests: created };
}
}

View File

@ -1,93 +1,93 @@

// import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto";
// import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto";
// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
// import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client";
// import { VacationService } from "src/modules/business-logics/services/vacation.service";
// import { PrismaService } from "src/prisma/prisma.service";
// import { mapRowToView } from "../mappers/leave-requests.mapper";
// import { leaveRequestsSelect } from "../utils/leave-requests.select";
// import { roundToQuarterHour } from "src/common/utils/date-utils";
// import { LeaveRequestsUtils } from "../utils/leave-request.util";
// import { normalizeDates, toDateOnly } from "src/modules/shared/helpers/date-time.helpers";
// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils";
import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common";
import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client";
import { roundToQuarterHour } from "src/common/utils/date-utils";
import { PrismaService } from "src/prisma/prisma.service";
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto";
import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto";
import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper";
import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util";
import { leaveRequestsSelect } from "src/time-and-attendance/modules/leave-requests/utils/leave-requests.select";
import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers";
import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils";
import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils";
// @Injectable()
// export class VacationLeaveRequestsService {
// constructor(
// private readonly prisma: PrismaService,
// private readonly vacationService: VacationService,
// private readonly leaveUtils: LeaveRequestsUtils,
// private readonly emailResolver: EmailToIdResolver,
// private readonly typeResolver: BankCodesResolver,
// ) {}
// async create(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
// const email = dto.email.trim();
// const employee_id = await this.emailResolver.findIdByEmail(email);
// const bank_code = await this.typeResolver.findByType(LeaveTypes.VACATION);
// if(!bank_code) throw new NotFoundException(`bank_code not found`);
@Injectable()
export class VacationLeaveRequestsService {
constructor(
private readonly prisma: PrismaService,
private readonly vacationService: VacationService,
private readonly leaveUtils: LeaveRequestsUtils,
private readonly emailResolver: EmailToIdResolver,
private readonly typeResolver: BankCodesResolver,
) {}
async create(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
const email = dto.email.trim();
const employee_id = await this.emailResolver.findIdByEmail(email);
const bank_code = await this.typeResolver.findByType(LeaveTypes.VACATION);
if(!bank_code) throw new NotFoundException(`bank_code not found`);
// const modifier = bank_code.modifier ?? 1;
// const dates = normalizeDates(dto.dates);
// const requested_hours_per_day = dto.requested_hours ?? 8;
// if (!dates.length) throw new BadRequestException("Dates array must not be empty");
const modifier = bank_code.modifier ?? 1;
const dates = normalizeDates(dto.dates);
const requested_hours_per_day = dto.requested_hours ?? 8;
if (!dates.length) throw new BadRequestException("Dates array must not be empty");
// const entries = dates
// .map((iso) => ({ iso, date: toDateOnly(iso) }))
// .sort((a, b) => a.date.getTime() - b.date.getTime());
// const start_date = entries[0].date;
// const total_payable_hours = await this.vacationService.calculateVacationPay(
// employee_id,
// start_date,
// entries.length,
// modifier,
// );
// let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours));
// const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier);
const entries = dates
.map((iso) => ({ iso, date: toDateOnly(iso) }))
.sort((a, b) => a.date.getTime() - b.date.getTime());
const start_date = entries[0].date;
const total_payable_hours = await this.vacationService.calculateVacationPay(
employee_id,
start_date,
entries.length,
modifier,
);
let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours));
const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier);
// const created: LeaveRequestViewDto[] = [];
const created: LeaveRequestViewDto[] = [];
// for (const { iso, date } of entries) {
// const existing = await this.prisma.leaveRequests.findUnique({
// where: {
// leave_per_employee_date: {
// employee_id: employee_id,
// leave_type: LeaveTypes.VACATION,
// date,
// },
// },
// select: { id: true },
// });
// if (existing) throw new BadRequestException(`Vacation request already exists for ${iso}`);
for (const { iso, date } of entries) {
const existing = await this.prisma.leaveRequests.findUnique({
where: {
leave_per_employee_date: {
employee_id: employee_id,
leave_type: LeaveTypes.VACATION,
date,
},
},
select: { id: true },
});
if (existing) throw new BadRequestException(`Vacation request already exists for ${iso}`);
// const payable = Math.min(remaining_payable_hours, daily_payable_cap);
// const payable_rounded = roundToQuarterHour(Math.max(0, payable));
// remaining_payable_hours = roundToQuarterHour(
// Math.max(0, remaining_payable_hours - payable_rounded),
// );
const payable = Math.min(remaining_payable_hours, daily_payable_cap);
const payable_rounded = roundToQuarterHour(Math.max(0, payable));
remaining_payable_hours = roundToQuarterHour(
Math.max(0, remaining_payable_hours - payable_rounded),
);
// const row = await this.prisma.leaveRequests.create({
// data: {
// employee_id: employee_id,
// bank_code_id: bank_code.id,
// payable_hours: payable_rounded,
// requested_hours: requested_hours_per_day,
// leave_type: LeaveTypes.VACATION,
// comment: dto.comment ?? "",
// approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING,
// date,
// },
// select: leaveRequestsSelect,
// });
const row = await this.prisma.leaveRequests.create({
data: {
employee_id: employee_id,
bank_code_id: bank_code.id,
payable_hours: payable_rounded,
requested_hours: requested_hours_per_day,
leave_type: LeaveTypes.VACATION,
comment: dto.comment ?? "",
approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING,
date,
},
select: leaveRequestsSelect,
});
// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
// if (row.approval_status === LeaveApprovalStatus.APPROVED) {
// await this.leaveUtils.syncShift(email, employee_id, iso, hours, LeaveTypes.VACATION, row.comment);
// }
// created.push({ ...mapRowToView(row), action: "create" });
// }
// return { action: "create", leave_requests: created };
// }
// }
const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
if (row.approval_status === LeaveApprovalStatus.APPROVED) {
// await this.leaveUtils.syncShift(email, employee_id, iso, hours, LeaveTypes.VACATION, row.comment);
}
created.push({ ...mapRowToView(row), action: "create" });
}
return { action: "create", leave_requests: created };
}
}

View File

@ -1,104 +1,106 @@
// import { hhmmFromLocal, toDateOnly, toStringFromDate } from "src/modules/shared/helpers/date-time.helpers";
// import { BadRequestException, Injectable } from "@nestjs/common";
// import { PrismaService } from "src/prisma/prisma.service";
// import { LeaveTypes } from "@prisma/client";
// import { UpsertAction } from "src/modules/shared/types/upsert-actions.types";
// @Injectable()
// export class LeaveRequestsUtils {
// constructor(
// private readonly prisma: PrismaService,
// private readonly shiftsCommand: ShiftsCommandService,
// ){}
import { BadRequestException, Injectable } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service";
import { LeaveTypes } from "@prisma/client";
import { toDateOnly, toStringFromDate, hhmmFromLocal } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers";
import { UpsertAction } from "src/time-and-attendance/modules/shared/types/upsert-actions.types";
import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service";
// async syncShift(
// email: string,
// employee_id: number,
// date: string,
// hours: number,
// type: LeaveTypes,
// comment?: string,
// ) {
// if (hours <= 0) return;
@Injectable()
export class LeaveRequestsUtils {
constructor(
private readonly prisma: PrismaService,
private readonly shiftsService: ShiftsUpsertService,
){}
// const duration_minutes = Math.round(hours * 60);
// if (duration_minutes > 8 * 60) {
// throw new BadRequestException("Amount of hours cannot exceed 8 hours per day.");
// }
// const date_only = toDateOnly(date);
// const yyyy_mm_dd = toStringFromDate(date_only);
async syncShift(
email: string,
employee_id: number,
date: string,
hours: number,
type: LeaveTypes,
comment?: string,
) {
if (hours <= 0) return;
const duration_minutes = Math.round(hours * 60);
if (duration_minutes > 8 * 60) {
throw new BadRequestException("Amount of hours cannot exceed 8 hours per day.");
}
const date_only = toDateOnly(date);
const yyyy_mm_dd = toStringFromDate(date_only);
// const start_minutes = 8 * 60;
// const end_minutes = start_minutes + duration_minutes;
// const toHHmm = (total: number) =>
// `${String(Math.floor(total / 60)).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`;
const start_minutes = 8 * 60;
const end_minutes = start_minutes + duration_minutes;
const toHHmm = (total: number) =>
`${String(Math.floor(total / 60)).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`;
// const existing = await this.prisma.shifts.findFirst({
// where: {
// date: date_only,
// bank_code: { type },
// timesheet: { employee_id: employee_id },
// },
// include: { bank_code: true },
// });
const existing = await this.prisma.shifts.findFirst({
where: {
date: date_only,
bank_code: { type },
timesheet: { employee_id: employee_id },
},
include: { bank_code: true },
});
// const action: UpsertAction = existing ? 'update' : 'create';
const action: UpsertAction = existing ? 'update' : 'create';
// await this.shiftsCommand.upsertShifts(email, action, {
// old_shift: existing
// ? {
// date: yyyy_mm_dd,
// start_time: existing.start_time.toISOString().slice(11, 16),
// end_time: existing.end_time.toISOString().slice(11, 16),
// type: existing.bank_code?.type ?? type,
// is_remote: existing.is_remote,
// is_approved:existing.is_approved,
// comment: existing.comment ?? undefined,
// }
// : undefined,
// new_shift: {
// date: yyyy_mm_dd,
// start_time: toHHmm(start_minutes),
// end_time: toHHmm(end_minutes),
// is_remote: existing?.is_remote ?? false,
// is_approved:existing?.is_approved ?? false,
// comment: comment ?? existing?.comment ?? "",
// type: type,
// },
// });
// }
// await this.shiftsService.upsertShifts(email, action, {
// old_shift: existing
// ? {
// date: yyyy_mm_dd,
// start_time: existing.start_time.toISOString().slice(11, 16),
// end_time: existing.end_time.toISOString().slice(11, 16),
// type: existing.bank_code?.type ?? type,
// is_remote: existing.is_remote,
// is_approved:existing.is_approved,
// comment: existing.comment ?? undefined,
// }
// : undefined,
// new_shift: {
// date: yyyy_mm_dd,
// start_time: toHHmm(start_minutes),
// end_time: toHHmm(end_minutes),
// is_remote: existing?.is_remote ?? false,
// is_approved:existing?.is_approved ?? false,
// comment: comment ?? existing?.comment ?? "",
// type: type,
// },
// });
}
// async removeShift(
// email: string,
// employee_id: number,
// iso_date: string,
// type: LeaveTypes,
// ) {
// const date_only = toDateOnly(iso_date);
// const yyyy_mm_dd = toStringFromDate(date_only);
// const existing = await this.prisma.shifts.findFirst({
// where: {
// date: date_only,
// bank_code: { type },
// timesheet: { employee_id: employee_id },
// },
// include: { bank_code: true },
// });
// if (!existing) return;
async removeShift(
email: string,
employee_id: number,
iso_date: string,
type: LeaveTypes,
) {
const date_only = toDateOnly(iso_date);
const yyyy_mm_dd = toStringFromDate(date_only);
const existing = await this.prisma.shifts.findFirst({
where: {
date: date_only,
bank_code: { type },
timesheet: { employee_id: employee_id },
},
include: { bank_code: true },
});
if (!existing) return;
// await this.shiftsCommand.upsertShifts(email, 'delete', {
// old_shift: {
// date: yyyy_mm_dd,
// start_time: hhmmFromLocal(existing.start_time),
// end_time: hhmmFromLocal(existing.end_time),
// type: existing.bank_code?.type ?? type,
// is_remote: existing.is_remote,
// is_approved:existing.is_approved,
// comment: existing.comment ?? undefined,
// },
// });
// }
// await this.shiftsService.upsertShifts(email, 'delete', {
// old_shift: {
// date: yyyy_mm_dd,
// start_time: hhmmFromLocal(existing.start_time),
// end_time: hhmmFromLocal(existing.end_time),
// type: existing.bank_code?.type ?? type,
// is_remote: existing.is_remote,
// is_approved:existing.is_approved,
// comment: existing.comment ?? undefined,
// },
// });
}
// }
}

View File

@ -1,20 +1,20 @@
import { Module } from "@nestjs/common";
import { ExpensesArchiveController } from "src/modules/archival/controllers/expenses-archive.controller";
import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module";
import { ExpenseController } from "src/time-and-attendance/modules/expenses/controllers/expense.controller";
import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service";
import { ExpensesArchivalService } from "src/time-and-attendance/modules/expenses/services/expenses-archival.service";
import { PayperiodsModule } from "src/time-and-attendance/modules/pay-period/pay-periods.module";
import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils";
import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils";
import { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller";
import { SchedulePresetsCommandService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service";
import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service";
import { SchedulePresetsQueryService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service";
import { ShiftController } from "src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller";
import { ShiftsGetService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service";
import { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller";
import { ExpensesArchivalService } from "src/time-and-attendance/modules/expenses/services/expenses-archival.service";
// import { LeaveRequestController } from "src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller";
import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module";
import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service";
import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service";
import { TimesheetController } from "src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller";
import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service";
import { ExpenseController } from "src/time-and-attendance/modules/expenses/controllers/expense.controller";
import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils";
import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils";
import { PayperiodsModule } from "src/time-and-attendance/modules/pay-period/pay-periods.module";
import { ShiftsGetService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service";
import { ShiftController } from "src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller";
import { Module } from "@nestjs/common";
@Module({
imports: [BusinessLogicsModule, PayperiodsModule],
@ -23,7 +23,6 @@ import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/ti
ShiftController,
SchedulePresetsController,
ExpenseController,
ExpensesArchiveController,
// LeaveRequestController,
],