From 2d69cfdb86a0622fd9c217dafdc7ac7530a89294 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 1 Aug 2025 14:54:09 -0400 Subject: [PATCH] feat(business-logic): implementation of business-logics services into their respective modules. --- src/app.module.ts | 20 ++--- src/business-logics/business-logics.module.ts | 32 ++++++++ .../services/after-hours.service.ts | 79 +++++++++++++++++++ .../services}/holiday.service.ts | 2 +- .../services/mileage.service.ts | 36 +++++++++ .../services}/overtime.service.ts | 2 +- .../services}/sick-leave.service.ts | 2 +- .../services}/vacation.service.ts | 6 +- src/modules/bank-codes/bank-codes.module.ts | 1 - src/modules/expenses/expenses.module.ts | 4 +- .../expenses/services/expenses.service.ts | 66 ++++++++-------- .../leave-requests/leave-requests.module.ts | 16 +--- .../services/leave-requests.service.ts | 6 +- src/modules/shifts/services/shifts.service.ts | 35 +------- src/modules/shifts/shifts.module.ts | 5 +- .../timesheets/services/timesheets.service.ts | 2 +- src/modules/timesheets/timesheets.module.ts | 8 +- 17 files changed, 218 insertions(+), 104 deletions(-) create mode 100644 src/business-logics/business-logics.module.ts create mode 100644 src/business-logics/services/after-hours.service.ts rename src/{business-logic => business-logics/services}/holiday.service.ts (97%) create mode 100644 src/business-logics/services/mileage.service.ts rename src/{business-logic => business-logics/services}/overtime.service.ts (98%) rename src/{business-logic => business-logics/services}/sick-leave.service.ts (97%) rename src/{business-logic => business-logics/services}/vacation.service.ts (98%) diff --git a/src/app.module.ts b/src/app.module.ts index b9c689c..bb77024 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -17,25 +17,27 @@ import { PayperiodsModule } from './modules/pay-periods/pay-periods.module'; import { ScheduleModule } from '@nestjs/schedule'; import { ArchivalModule } from './modules/archival/archival.module'; import { BankCodesModule } from './modules/bank-codes/bank-codes.module'; -import { OvertimeService } from './business-logic/overtime.service'; +import { OvertimeService } from './business-logics/services/overtime.service'; +import { BusinessLogicsModule } from './business-logics/business-logics.module'; @Module({ imports: [ ScheduleModule.forRoot(), - BankCodesModule, ArchivalModule, - PrismaModule, - HealthModule, - UsersModule, - OauthAccessTokensModule, + AuthenticationModule, + BankCodesModule, + BusinessLogicsModule, CustomersModule, EmployeesModule, - LeaveRequestsModule, ExpensesModule, + HealthModule, + LeaveRequestsModule, + OauthAccessTokensModule, + PayperiodsModule, + PrismaModule, ShiftsModule, TimesheetsModule, - AuthenticationModule, - PayperiodsModule, + UsersModule, ], controllers: [AppController, HealthController], providers: [AppService, OvertimeService], diff --git a/src/business-logics/business-logics.module.ts b/src/business-logics/business-logics.module.ts new file mode 100644 index 0000000..f84330d --- /dev/null +++ b/src/business-logics/business-logics.module.ts @@ -0,0 +1,32 @@ +import { Module } from "@nestjs/common"; +import { PrismaService } from "src/prisma/prisma.service"; +//import { AfterHoursService } from "./services/after-hours.service"; +import { HolidayService } from "./services/holiday.service"; +import { OvertimeService } from "./services/overtime.service"; +import { SickLeaveService } from "./services/sick-leave.service"; +import { VacationService } from "./services/vacation.service"; +import { MileageService } from "./services/mileage.service"; + + +//AfterHours is not used, need to clarify infos before implementing into shifts.service +@Module({ + providers: [ + PrismaService, + //AfterHoursService, + HolidayService, + MileageService, + OvertimeService, + SickLeaveService, + VacationService + ], + exports: [ + //AfterHoursService, + HolidayService, + MileageService, + OvertimeService, + SickLeaveService, + VacationService, + ], +}) + +export class BusinessLogicsModule {} \ No newline at end of file diff --git a/src/business-logics/services/after-hours.service.ts b/src/business-logics/services/after-hours.service.ts new file mode 100644 index 0000000..d9aeadf --- /dev/null +++ b/src/business-logics/services/after-hours.service.ts @@ -0,0 +1,79 @@ +import { BadRequestException, Injectable, Logger } from "@nestjs/common"; +import { PrismaService } from "../../prisma/prisma.service"; + + +//THIS SERVICE IS NOT USED RULES TO BE DETERMINED WITH MIKE/HR/ACCOUNTING +@Injectable() +export class AfterHoursService { + private readonly logger = new Logger(AfterHoursService.name); + private static readonly BUSINESS_START = 7; + private static readonly BUSINESS_END = 18; + private static readonly ROUND_MINUTES = 15; + + constructor(private readonly prisma: PrismaService) {} + + + private getPreBusinessMinutes(start: Date, end: Date): number { + const bizStart = new Date(start); + bizStart.setHours(AfterHoursService.BUSINESS_START, 0,0,0); + + if (end>= start || start >= bizStart) { + return 0; + } + + const segmentEnd = end < bizStart ? end : bizStart; + const minutes = (segmentEnd.getTime() - start.getTime()) / 60000; + + this.logger.debug(`getPreBusinessMintutes -> ${minutes.toFixed(1)}min`); + return minutes; + + } + + private getPostBusinessMinutes(start: Date, end: Date): number { + const bizEnd = new Date(start); + bizEnd.setHours(AfterHoursService.BUSINESS_END,0,0,0); + + if( end <= bizEnd ) { + return 0; + } + + const segmentStart = start > bizEnd ? start : bizEnd; + const minutes = (end.getTime() - segmentStart.getTime()) / 60000; + + this.logger.debug(`getPostBusinessMintutes -> ${minutes.toFixed(1)}min`); + return minutes; + + } + + private roundToNearestQUarterMinute(minutes: number): number { + const rounded = Math.round(minutes / AfterHoursService.ROUND_MINUTES) + * AfterHoursService.ROUND_MINUTES; + this.logger.debug(`roundToNearestQuarterMinute -> raw=${minutes.toFixed(1)}min, rounded= ${rounded}min`); + return rounded; + } + + public computeAfterHours(start: Date, end:Date): number { + if(end.getTime() <= start.getTime()) { + throw new BadRequestException('The end cannot be before the starting of the shift'); + } + + if (start.toDateString() !== end.toDateString()) { + throw new BadRequestException('you cannot enter a shift that start in a day and end in the next' + + 'You must create 2 instances, one on the first day and the second during the next day.'); + } + + const preMin = this.getPreBusinessMinutes(start, end); + const postMin = this.getPostBusinessMinutes(start, end); + const rawAftermin = preMin + postMin; + + const roundedMin = this.roundToNearestQUarterMinute(rawAftermin); + + const hours = roundedMin / 60; + const result = parseFloat(hours.toFixed(2)); + + this.logger.debug(`computeAfterHours -> rawAfterMin= ${rawAftermin.toFixed(1)}min, + + rounded = ${roundedMin}min, hours = ${result.toFixed(2)}`); + return result; + } +} + diff --git a/src/business-logic/holiday.service.ts b/src/business-logics/services/holiday.service.ts similarity index 97% rename from src/business-logic/holiday.service.ts rename to src/business-logics/services/holiday.service.ts index 235cc8e..dece70c 100644 --- a/src/business-logic/holiday.service.ts +++ b/src/business-logics/services/holiday.service.ts @@ -1,5 +1,5 @@ import { Injectable, Logger } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "../../prisma/prisma.service"; @Injectable() export class HolidayService { diff --git a/src/business-logics/services/mileage.service.ts b/src/business-logics/services/mileage.service.ts new file mode 100644 index 0000000..b233812 --- /dev/null +++ b/src/business-logics/services/mileage.service.ts @@ -0,0 +1,36 @@ +import { BadRequestException, Injectable, Logger } from "@nestjs/common"; +import { PrismaService } from "../../prisma/prisma.service"; +import { Decimal } from "@prisma/client/runtime/library"; + +@Injectable() +export class MileageService { + private readonly logger = new Logger(MileageService.name); + + constructor(private readonly prisma: PrismaService) {} + + public async calculateReimbursement(amount: number, bankCodeId: number): Promise { + if(amount < 0) { + throw new BadRequestException(`The amount most be higher than 0`); + } + + //fetch modifier + const bankCode = await this.prisma.bankCodes.findUnique({ + where: { id: bankCodeId }, + select: { modifier: true, type: true }, + }); + + if(!bankCode) { + throw new BadRequestException(`bank_code ${bankCodeId} not found`); + } + if(bankCode.type !== 'mileage') { + this.logger.warn(`bank_code ${bankCodeId} of type ${bankCode.type} is used for mileage`) + } + + //calculate total amount to reimburs + const reimboursement = amount * bankCode.modifier; + const result = parseFloat(reimboursement.toFixed(2)); + this.logger.debug(`calculateReimbursement -> amount= ${amount}, modifier= ${bankCode.modifier}, total= ${result}`); + return result; + } + +} \ No newline at end of file diff --git a/src/business-logic/overtime.service.ts b/src/business-logics/services/overtime.service.ts similarity index 98% rename from src/business-logic/overtime.service.ts rename to src/business-logics/services/overtime.service.ts index b99122b..5d685a5 100644 --- a/src/business-logic/overtime.service.ts +++ b/src/business-logics/services/overtime.service.ts @@ -1,5 +1,5 @@ import { Injectable, Logger } from '@nestjs/common'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { PrismaService } from '../../prisma/prisma.service'; @Injectable() export class OvertimeService { diff --git a/src/business-logic/sick-leave.service.ts b/src/business-logics/services/sick-leave.service.ts similarity index 97% rename from src/business-logic/sick-leave.service.ts rename to src/business-logics/services/sick-leave.service.ts index 2057e05..fcbe5e8 100644 --- a/src/business-logic/sick-leave.service.ts +++ b/src/business-logics/services/sick-leave.service.ts @@ -1,5 +1,5 @@ import { Injectable, Logger } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "../../prisma/prisma.service"; @Injectable() export class SickLeaveService { diff --git a/src/business-logic/vacation.service.ts b/src/business-logics/services/vacation.service.ts similarity index 98% rename from src/business-logic/vacation.service.ts rename to src/business-logics/services/vacation.service.ts index 094b64d..f726b6e 100644 --- a/src/business-logic/vacation.service.ts +++ b/src/business-logics/services/vacation.service.ts @@ -1,12 +1,12 @@ import { Injectable, Logger, NotFoundException } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "../../prisma/prisma.service"; @Injectable() export class VacationService { constructor(private readonly prisma: PrismaService) {} - private readonly logger = new Logger(VacationService.name); -/** + + /** * Calculate the ammount allowed for vacation days. * * @param employeeId employee ID diff --git a/src/modules/bank-codes/bank-codes.module.ts b/src/modules/bank-codes/bank-codes.module.ts index bda3b9b..0030ec7 100644 --- a/src/modules/bank-codes/bank-codes.module.ts +++ b/src/modules/bank-codes/bank-codes.module.ts @@ -1,5 +1,4 @@ import { Module } from "@nestjs/common"; - import { PrismaService } from "src/prisma/prisma.service"; import { BankCodesControllers } from "./controllers/bank-codes.controller"; import { BankCodesService } from "./services/bank-codes.services"; diff --git a/src/modules/expenses/expenses.module.ts b/src/modules/expenses/expenses.module.ts index 08cf449..0171a3e 100644 --- a/src/modules/expenses/expenses.module.ts +++ b/src/modules/expenses/expenses.module.ts @@ -2,10 +2,12 @@ import { PrismaService } from "src/prisma/prisma.service"; import { ExpensesController } from "./controllers/expenses.controller"; import { Module } from "@nestjs/common"; import { ExpensesService } from "./services/expenses.service"; +import { BusinessLogicsModule } from "src/business-logics/business-logics.module"; @Module({ + imports: [BusinessLogicsModule], controllers: [ExpensesController], - providers: [ExpensesService, PrismaService], + providers: [ExpensesService], exports: [ ExpensesService ], }) diff --git a/src/modules/expenses/services/expenses.service.ts b/src/modules/expenses/services/expenses.service.ts index a5b00b2..6b803ca 100644 --- a/src/modules/expenses/services/expenses.service.ts +++ b/src/modules/expenses/services/expenses.service.ts @@ -3,48 +3,55 @@ import { PrismaService } from "src/prisma/prisma.service"; import { CreateExpenseDto } from "../dtos/create-expense"; import { Expenses, ExpensesArchive } from "@prisma/client"; import { UpdateExpenseDto } from "../dtos/update-expense"; +import { MileageService } from "src/business-logics/services/mileage.service"; @Injectable() export class ExpensesService { - constructor(private readonly prisma: PrismaService) {} + constructor( + private readonly prisma: PrismaService, + private readonly mileageService: MileageService, + ) {} async create(dto: CreateExpenseDto): Promise { - const { timesheet_id, bank_code_id, date, amount, + const { timesheet_id, bank_code_id, date, amount:rawAmount, description, is_approved,supervisor_comment} = dto; - return this.prisma.expenses.create({ - data: { timesheet_id, bank_code_id,date,amount,description,is_approved,supervisor_comment}, - include: { - timesheet: { - include: { - employee: { include: { user: true } }, - }, - }, - bank_code: true, - }, + + + //fetches type and modifier + const bankCode = await this.prisma.bankCodes.findUnique({ + where: { id: bank_code_id }, + select: { type: true, modifier: true }, }); + if(!bankCode) { + throw new NotFoundException(`bank_code #${bank_code_id} not found`) + } + + //if mileage -> service, otherwise the ratio is amount:1 + let finalAmount: number; + if(bankCode.type === 'mileage') { + finalAmount = await this.mileageService.calculateReimbursement(rawAmount, bank_code_id); + }else { + finalAmount = parseFloat( (rawAmount * bankCode.modifier).toFixed(2)); + } + + return this.prisma.expenses.create({ + data: { timesheet_id, bank_code_id, date, amount: finalAmount, description, is_approved, supervisor_comment}, + include: { timesheet: { include: { employee: { include: { user: true }}}}, + bank_code: true, + }, + }) } findAll(): Promise { return this.prisma.expenses.findMany({ - include: { - timesheet: { - include: { - employee: { include: { user: true } }, - }, - }, - }, + include: { timesheet: { include: { employee: { include: { user: true } } } } }, }); } async findOne(id: number): Promise { const expense = await this.prisma.expenses.findUnique({ where: { id }, - include: { - timesheet: { - include: { - employee: { include: { user:true } }, - }, - }, + include: { timesheet: { include: { employee: { include: { user:true } } } }, bank_code: true, }, }); @@ -69,14 +76,7 @@ export class ExpensesService { ...(is_approved !== undefined && { is_approved }), ...(supervisor_comment !== undefined && { supervisor_comment }), }, - include: { - timesheet: { - include: { - employee: { - include: { user: true } - }, - }, - }, + include: { timesheet: { include: { employee: { include: { user: true } } } }, bank_code: true, }, }); diff --git a/src/modules/leave-requests/leave-requests.module.ts b/src/modules/leave-requests/leave-requests.module.ts index f000f18..df86084 100644 --- a/src/modules/leave-requests/leave-requests.module.ts +++ b/src/modules/leave-requests/leave-requests.module.ts @@ -1,21 +1,13 @@ -import { PrismaService } from "src/prisma/prisma.service"; import { LeaveRequestController } from "./controllers/leave-requests.controller"; import { LeaveRequestsService } from "./services/leave-requests.service"; import { Module } from "@nestjs/common"; -import { HolidayService } from "src/business-logic/holiday.service"; -import { VacationService } from "src/business-logic/vacation.service"; -import { SickLeaveService } from "src/business-logic/sick-leave.service"; +import { BusinessLogicsModule } from "src/business-logics/business-logics.module"; @Module({ + imports: [BusinessLogicsModule], controllers: [LeaveRequestController], - providers: [ - LeaveRequestsService, - PrismaService, - HolidayService, - VacationService, - SickLeaveService, - ], - exports: [ LeaveRequestsService], + providers: [LeaveRequestsService], + exports: [LeaveRequestsService], }) export class LeaveRequestsModule {} \ No newline at end of file diff --git a/src/modules/leave-requests/services/leave-requests.service.ts b/src/modules/leave-requests/services/leave-requests.service.ts index 9ba05cc..3abcbe0 100644 --- a/src/modules/leave-requests/services/leave-requests.service.ts +++ b/src/modules/leave-requests/services/leave-requests.service.ts @@ -3,9 +3,9 @@ import { PrismaService } from "src/prisma/prisma.service"; import { CreateLeaveRequestsDto } from "../dtos/create-leave-requests.dto"; import { LeaveRequests, LeaveRequestsArchive } from "@prisma/client"; import { UpdateLeaveRequestsDto } from "../dtos/update-leave-requests.dto"; -import { HolidayService } from "src/business-logic/holiday.service"; -import { VacationService } from "src/business-logic/vacation.service"; -import { SickLeaveService } from "src/business-logic/sick-leave.service"; +import { HolidayService } from "src/business-logics/services/holiday.service"; +import { SickLeaveService } from "src/business-logics/services/sick-leave.service"; +import { VacationService } from "src/business-logics/services/vacation.service"; @Injectable() export class LeaveRequestsService { diff --git a/src/modules/shifts/services/shifts.service.ts b/src/modules/shifts/services/shifts.service.ts index e7c3942..d1961c0 100644 --- a/src/modules/shifts/services/shifts.service.ts +++ b/src/modules/shifts/services/shifts.service.ts @@ -12,12 +12,7 @@ export class ShiftsService { const { timesheet_id, bank_code_id, date, start_time, end_time } = dto; return this.prisma.shifts.create({ data: { timesheet_id, bank_code_id, date, start_time, end_time }, - include: { - timesheet: { - include: { - employee: { include: { user: true } }, - }, - }, + include: { timesheet: { include: { employee: { include: { user: true } } } }, bank_code: true, }, }); @@ -25,29 +20,14 @@ export class ShiftsService { findAll(): Promise { return this.prisma.shifts.findMany({ - include: { - timesheet: { - include: { - employee: { - include: { user:true } - }, - }, - }, - }, + include: { timesheet: { include: { employee: { include: { user:true } } } } }, }); } async findOne(id: number): Promise { const shift = await this.prisma.shifts.findUnique({ where: { id }, - include: { - timesheet: { - include: { - employee: { - include: { user: true } - }, - }, - }, + include: { timesheet: { include: { employee: { include: { user: true } } } }, bank_code: true, }, }); @@ -69,14 +49,7 @@ export class ShiftsService { ...(start_time !== undefined && { start_time }), ...(end_time !== undefined && { end_time }), }, - include: { - timesheet: { - include: { - employee: { - include: { user: true } - }, - }, - }, + include: { timesheet: { include: { employee: { include: { user: true } } } }, bank_code: true, }, }); diff --git a/src/modules/shifts/shifts.module.ts b/src/modules/shifts/shifts.module.ts index 53924f0..bec2737 100644 --- a/src/modules/shifts/shifts.module.ts +++ b/src/modules/shifts/shifts.module.ts @@ -1,11 +1,12 @@ import { Module } from '@nestjs/common'; import { ShiftsController } from './controllers/shifts.controller'; import { ShiftsService } from './services/shifts.service'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { BusinessLogicsModule } from 'src/business-logics/business-logics.module'; @Module({ + imports: [BusinessLogicsModule], controllers: [ShiftsController], - providers: [ShiftsService, PrismaService], + providers: [ShiftsService], exports: [ShiftsService], }) export class ShiftsModule {} diff --git a/src/modules/timesheets/services/timesheets.service.ts b/src/modules/timesheets/services/timesheets.service.ts index 4cc519d..4beea6b 100644 --- a/src/modules/timesheets/services/timesheets.service.ts +++ b/src/modules/timesheets/services/timesheets.service.ts @@ -3,7 +3,7 @@ import { PrismaService } from 'src/prisma/prisma.service'; import { CreateTimesheetDto } from '../dtos/create-timesheet.dto'; import { Timesheets, TimesheetsArchive } from '@prisma/client'; import { UpdateTimesheetDto } from '../dtos/update-timesheet.dto'; -import { OvertimeService } from 'src/business-logic/overtime.service'; +import { OvertimeService } from 'src/business-logics/services/overtime.service'; @Injectable() export class TimesheetsService { diff --git a/src/modules/timesheets/timesheets.module.ts b/src/modules/timesheets/timesheets.module.ts index 7979a2b..1ab62dd 100644 --- a/src/modules/timesheets/timesheets.module.ts +++ b/src/modules/timesheets/timesheets.module.ts @@ -1,14 +1,12 @@ import { Module } from '@nestjs/common'; import { TimesheetsController } from './controllers/timesheets.controller'; import { TimesheetsService } from './services/timesheets.service'; -import { OvertimeService } from 'src/business-logic/overtime.service'; +import { BusinessLogicsModule } from 'src/business-logics/business-logics.module'; @Module({ + imports: [BusinessLogicsModule], controllers: [TimesheetsController], - providers: [ - TimesheetsService, - OvertimeService, - ], + providers: [ TimesheetsService ], exports: [TimesheetsService], }) export class TimesheetsModule {}