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" "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": { "info": {

View File

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

View File

@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { UsersService } from 'src/modules/users-management/services/users.service'; import { UsersService } from 'src/identity-and-account/users-management/services/users.service';
@Injectable() @Injectable()
export class AuthentikAuthService { export class AuthentikAuthService {

View File

@ -1,13 +1,12 @@
// import { Module } from '@nestjs/common'; // import { Module } from '@nestjs/common';
// import { EmployeesController } from './controllers/employees.controller'; // import { EmployeesController } from './controllers/employees.controller';
// import { EmployeesService } from './services/employees.service'; // import { EmployeesService } from './services/employees.service';
// import { EmployeesArchivalService } from './services/employees-archival.service';
// import { SharedModule } from '../../time-and-attendance/modules/shared/shared.module'; // import { SharedModule } from '../../time-and-attendance/modules/shared/shared.module';
// @Module({ // @Module({
// imports: [SharedModule], // imports: [SharedModule],
// controllers: [EmployeesController], // controllers: [EmployeesController],
// providers: [EmployeesService, EmployeesArchivalService], // providers: [EmployeesService],
// exports: [EmployeesService, EmployeesArchivalService], // exports: [EmployeesService ],
// }) // })
// export class EmployeesModule {} // 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 { Body, Controller, Post } from "@nestjs/common";
// import { ApiBearerAuth, ApiTags } from "@nestjs/swagger"; import { ApiBearerAuth, ApiTags } from "@nestjs/swagger";
// import { LeaveRequestsService } from "../services/leave-request.service"; import { LeaveRequestsService } from "../services/leave-request.service";
// import { UpsertLeaveRequestDto } from "../dtos/upsert-leave-request.dto"; import { UpsertLeaveRequestDto } from "../dtos/upsert-leave-request.dto";
// import { LeaveTypes } from "@prisma/client"; import { LeaveTypes } from "@prisma/client";
// @ApiTags('Leave Requests') @ApiTags('Leave Requests')
// @ApiBearerAuth('access-token') @ApiBearerAuth('access-token')
// // @UseGuards() // @UseGuards()
// @Controller('leave-requests') @Controller('leave-requests')
// export class LeaveRequestController { export class LeaveRequestController {
// constructor(private readonly leave_service: LeaveRequestsService){} constructor(private readonly leave_service: LeaveRequestsService){}
// @Post('upsert') @Post('upsert')
// async upsertLeaveRequest(@Body() dto: UpsertLeaveRequestDto) { async upsertLeaveRequest(@Body() dto: UpsertLeaveRequestDto) {
// const { action, leave_requests } = await this.leave_service.handle(dto); const { action, leave_requests } = await this.leave_service.handle(dto);
// return { action, leave_requests }; return { action, leave_requests };
// }q }q
// //TODO: //TODO:
// /* /*
// @Get('archive') @Get('archive')
// findAllArchived(){...} findAllArchived(){...}
// @Get('archive/:id') @Get('archive/:id')
// findOneArchived(id){...} findOneArchived(id){...}
// */ */
// } }

View File

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

View File

@ -1,98 +1,99 @@
// import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto"; import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common";
// import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client";
// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; import { roundToQuarterHour } from "src/common/utils/date-utils";
// import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service";
// import { leaveRequestsSelect } from "../utils/leave-requests.select"; import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
// import { mapRowToView } from "../mappers/leave-requests.mapper"; import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto";
// import { PrismaService } from "src/prisma/prisma.service"; import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto";
// import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service"; import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper";
// import { roundToQuarterHour } from "src/common/utils/date-utils"; import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util";
// import { LeaveRequestsUtils } from "../utils/leave-request.util"; import { leaveRequestsSelect } from "src/time-and-attendance/modules/leave-requests/utils/leave-requests.select";
// import { normalizeDates, toDateOnly } from "src/modules/shared/helpers/date-time.helpers"; import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers";
// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils"; import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils";
// import { BankCodesResolver } from "src/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> { @Injectable()
// const email = dto.email.trim(); export class SickLeaveRequestsService {
// const employee_id = await this.emailResolver.findIdByEmail(email); constructor(
// const bank_code = await this.typeResolver.findByType(LeaveTypes.SICK); private readonly prisma: PrismaService,
// if(!bank_code) throw new NotFoundException(`bank_code not found`); private readonly sickService: SickLeaveService,
private readonly leaveUtils: LeaveRequestsUtils,
private readonly emailResolver: EmailToIdResolver,
private readonly typeResolver: BankCodesResolver,
) {}
// const modifier = bank_code.modifier ?? 1; async create(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
// const dates = normalizeDates(dto.dates); const email = dto.email.trim();
// if (!dates.length) throw new BadRequestException("Dates array must not be empty"); const employee_id = await this.emailResolver.findIdByEmail(email);
// const requested_hours_per_day = dto.requested_hours ?? 8; const bank_code = await this.typeResolver.findByType(LeaveTypes.SICK);
if(!bank_code) throw new NotFoundException(`bank_code not found`);
// const entries = dates.map((iso) => ({ iso, date: toDateOnly(iso) })); const modifier = bank_code.modifier ?? 1;
// const reference_date = entries.reduce( const dates = normalizeDates(dto.dates);
// (latest, entry) => (entry.date > latest ? entry.date : latest), if (!dates.length) throw new BadRequestException("Dates array must not be empty");
// entries[0].date, const requested_hours_per_day = dto.requested_hours ?? 8;
// );
// 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 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);
// for (const { iso, date } of entries) { const created: LeaveRequestViewDto[] = [];
// 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); for (const { iso, date } of entries) {
// const payable_rounded = roundToQuarterHour(Math.max(0, payable)); const existing = await this.prisma.leaveRequests.findUnique({
// remaining_payable_hours = roundToQuarterHour( where: {
// Math.max(0, remaining_payable_hours - payable_rounded), 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 row = await this.prisma.leaveRequests.create({ const payable = Math.min(remaining_payable_hours, daily_payable_cap);
// data: { const payable_rounded = roundToQuarterHour(Math.max(0, payable));
// employee_id: employee_id, remaining_payable_hours = roundToQuarterHour(
// bank_code_id: bank_code.id, Math.max(0, remaining_payable_hours - payable_rounded),
// 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); const row = await this.prisma.leaveRequests.create({
// if (row.approval_status === LeaveApprovalStatus.APPROVED) { 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); // 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 { Injectable, NotFoundException, BadRequestException } from "@nestjs/common";
// import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto"; import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client";
// import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; import { roundToQuarterHour } from "src/common/utils/date-utils";
// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service";
// import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
// import { VacationService } from "src/modules/business-logics/services/vacation.service"; import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto";
// import { PrismaService } from "src/prisma/prisma.service"; import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto";
// import { mapRowToView } from "../mappers/leave-requests.mapper"; import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper";
// import { leaveRequestsSelect } from "../utils/leave-requests.select"; import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util";
// import { roundToQuarterHour } from "src/common/utils/date-utils"; import { leaveRequestsSelect } from "src/time-and-attendance/modules/leave-requests/utils/leave-requests.select";
// import { LeaveRequestsUtils } from "../utils/leave-request.util"; import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers";
// import { normalizeDates, toDateOnly } from "src/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/modules/shared/utils/resolve-email-id.utils"; import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils";
// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-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> { @Injectable()
// const email = dto.email.trim(); export class VacationLeaveRequestsService {
// const employee_id = await this.emailResolver.findIdByEmail(email); constructor(
// const bank_code = await this.typeResolver.findByType(LeaveTypes.VACATION); private readonly prisma: PrismaService,
// if(!bank_code) throw new NotFoundException(`bank_code not found`); private readonly vacationService: VacationService,
private readonly leaveUtils: LeaveRequestsUtils,
private readonly emailResolver: EmailToIdResolver,
private readonly typeResolver: BankCodesResolver,
) {}
// const modifier = bank_code.modifier ?? 1; async create(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
// const dates = normalizeDates(dto.dates); const email = dto.email.trim();
// const requested_hours_per_day = dto.requested_hours ?? 8; const employee_id = await this.emailResolver.findIdByEmail(email);
// if (!dates.length) throw new BadRequestException("Dates array must not be empty"); const bank_code = await this.typeResolver.findByType(LeaveTypes.VACATION);
if(!bank_code) throw new NotFoundException(`bank_code not found`);
// const entries = dates const modifier = bank_code.modifier ?? 1;
// .map((iso) => ({ iso, date: toDateOnly(iso) })) const dates = normalizeDates(dto.dates);
// .sort((a, b) => a.date.getTime() - b.date.getTime()); const requested_hours_per_day = dto.requested_hours ?? 8;
// const start_date = entries[0].date; if (!dates.length) throw new BadRequestException("Dates array must not be empty");
// 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 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);
// for (const { iso, date } of entries) { const created: LeaveRequestViewDto[] = [];
// 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); for (const { iso, date } of entries) {
// const payable_rounded = roundToQuarterHour(Math.max(0, payable)); const existing = await this.prisma.leaveRequests.findUnique({
// remaining_payable_hours = roundToQuarterHour( where: {
// Math.max(0, remaining_payable_hours - payable_rounded), 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 row = await this.prisma.leaveRequests.create({ const payable = Math.min(remaining_payable_hours, daily_payable_cap);
// data: { const payable_rounded = roundToQuarterHour(Math.max(0, payable));
// employee_id: employee_id, remaining_payable_hours = roundToQuarterHour(
// bank_code_id: bank_code.id, Math.max(0, remaining_payable_hours - payable_rounded),
// 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); const row = await this.prisma.leaveRequests.create({
// if (row.approval_status === LeaveApprovalStatus.APPROVED) { 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); // await this.leaveUtils.syncShift(email, employee_id, iso, hours, LeaveTypes.VACATION, 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,52 +1,54 @@
// 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() import { BadRequestException, Injectable } from "@nestjs/common";
// export class LeaveRequestsUtils { import { PrismaService } from "src/prisma/prisma.service";
// constructor( import { LeaveTypes } from "@prisma/client";
// private readonly prisma: PrismaService, import { toDateOnly, toStringFromDate, hhmmFromLocal } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers";
// private readonly shiftsCommand: ShiftsCommandService, 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( @Injectable()
// email: string, export class LeaveRequestsUtils {
// employee_id: number, constructor(
// date: string, private readonly prisma: PrismaService,
// hours: number, private readonly shiftsService: ShiftsUpsertService,
// type: LeaveTypes, ){}
// comment?: string,
// ) {
// if (hours <= 0) return;
// const duration_minutes = Math.round(hours * 60); async syncShift(
// if (duration_minutes > 8 * 60) { email: string,
// throw new BadRequestException("Amount of hours cannot exceed 8 hours per day."); employee_id: number,
// } date: string,
// const date_only = toDateOnly(date); hours: number,
// const yyyy_mm_dd = toStringFromDate(date_only); 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 start_minutes = 8 * 60;
// const end_minutes = start_minutes + duration_minutes; const end_minutes = start_minutes + duration_minutes;
// const toHHmm = (total: number) => const toHHmm = (total: number) =>
// `${String(Math.floor(total / 60)).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`; `${String(Math.floor(total / 60)).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`;
// const existing = await this.prisma.shifts.findFirst({ const existing = await this.prisma.shifts.findFirst({
// where: { where: {
// date: date_only, date: date_only,
// bank_code: { type }, bank_code: { type },
// timesheet: { employee_id: employee_id }, timesheet: { employee_id: employee_id },
// }, },
// include: { bank_code: true }, include: { bank_code: true },
// }); });
// const action: UpsertAction = existing ? 'update' : 'create'; const action: UpsertAction = existing ? 'update' : 'create';
// await this.shiftsCommand.upsertShifts(email, action, { // await this.shiftsService.upsertShifts(email, action, {
// old_shift: existing // old_shift: existing
// ? { // ? {
// date: yyyy_mm_dd, // date: yyyy_mm_dd,
@ -68,27 +70,27 @@
// type: type, // type: type,
// }, // },
// }); // });
// } }
// async removeShift( async removeShift(
// email: string, email: string,
// employee_id: number, employee_id: number,
// iso_date: string, iso_date: string,
// type: LeaveTypes, type: LeaveTypes,
// ) { ) {
// const date_only = toDateOnly(iso_date); const date_only = toDateOnly(iso_date);
// const yyyy_mm_dd = toStringFromDate(date_only); const yyyy_mm_dd = toStringFromDate(date_only);
// const existing = await this.prisma.shifts.findFirst({ const existing = await this.prisma.shifts.findFirst({
// where: { where: {
// date: date_only, date: date_only,
// bank_code: { type }, bank_code: { type },
// timesheet: { employee_id: employee_id }, timesheet: { employee_id: employee_id },
// }, },
// include: { bank_code: true }, include: { bank_code: true },
// }); });
// if (!existing) return; if (!existing) return;
// await this.shiftsCommand.upsertShifts(email, 'delete', { // await this.shiftsService.upsertShifts(email, 'delete', {
// old_shift: { // old_shift: {
// date: yyyy_mm_dd, // date: yyyy_mm_dd,
// start_time: hhmmFromLocal(existing.start_time), // start_time: hhmmFromLocal(existing.start_time),
@ -99,6 +101,6 @@
// comment: existing.comment ?? undefined, // 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 { 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 { 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 { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller";
import { ShiftsGetService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service"; 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 { 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 { 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({ @Module({
imports: [BusinessLogicsModule, PayperiodsModule], imports: [BusinessLogicsModule, PayperiodsModule],
@ -23,7 +23,6 @@ import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/ti
ShiftController, ShiftController,
SchedulePresetsController, SchedulePresetsController,
ExpenseController, ExpenseController,
ExpensesArchiveController,
// LeaveRequestController, // LeaveRequestController,
], ],