feat(business-logic): implementation of business-logics services into their respective modules.

This commit is contained in:
Matthieu Haineault 2025-08-01 14:54:09 -04:00
parent 5766715d77
commit 2d69cfdb86
17 changed files with 218 additions and 104 deletions

View File

@ -17,25 +17,27 @@ import { PayperiodsModule } from './modules/pay-periods/pay-periods.module';
import { ScheduleModule } from '@nestjs/schedule'; import { ScheduleModule } from '@nestjs/schedule';
import { ArchivalModule } from './modules/archival/archival.module'; import { ArchivalModule } from './modules/archival/archival.module';
import { BankCodesModule } from './modules/bank-codes/bank-codes.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({ @Module({
imports: [ imports: [
ScheduleModule.forRoot(), ScheduleModule.forRoot(),
BankCodesModule,
ArchivalModule, ArchivalModule,
PrismaModule, AuthenticationModule,
HealthModule, BankCodesModule,
UsersModule, BusinessLogicsModule,
OauthAccessTokensModule,
CustomersModule, CustomersModule,
EmployeesModule, EmployeesModule,
LeaveRequestsModule,
ExpensesModule, ExpensesModule,
HealthModule,
LeaveRequestsModule,
OauthAccessTokensModule,
PayperiodsModule,
PrismaModule,
ShiftsModule, ShiftsModule,
TimesheetsModule, TimesheetsModule,
AuthenticationModule, UsersModule,
PayperiodsModule,
], ],
controllers: [AppController, HealthController], controllers: [AppController, HealthController],
providers: [AppService, OvertimeService], providers: [AppService, OvertimeService],

View File

@ -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 {}

View File

@ -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;
}
}

View File

@ -1,5 +1,5 @@
import { Injectable, Logger } from "@nestjs/common"; import { Injectable, Logger } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service"; import { PrismaService } from "../../prisma/prisma.service";
@Injectable() @Injectable()
export class HolidayService { export class HolidayService {

View File

@ -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<number> {
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;
}
}

View File

@ -1,5 +1,5 @@
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from '../../prisma/prisma.service';
@Injectable() @Injectable()
export class OvertimeService { export class OvertimeService {

View File

@ -1,5 +1,5 @@
import { Injectable, Logger } from "@nestjs/common"; import { Injectable, Logger } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service"; import { PrismaService } from "../../prisma/prisma.service";
@Injectable() @Injectable()
export class SickLeaveService { export class SickLeaveService {

View File

@ -1,12 +1,12 @@
import { Injectable, Logger, NotFoundException } from "@nestjs/common"; import { Injectable, Logger, NotFoundException } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service"; import { PrismaService } from "../../prisma/prisma.service";
@Injectable() @Injectable()
export class VacationService { export class VacationService {
constructor(private readonly prisma: PrismaService) {} constructor(private readonly prisma: PrismaService) {}
private readonly logger = new Logger(VacationService.name); private readonly logger = new Logger(VacationService.name);
/**
/**
* Calculate the ammount allowed for vacation days. * Calculate the ammount allowed for vacation days.
* *
* @param employeeId employee ID * @param employeeId employee ID

View File

@ -1,5 +1,4 @@
import { Module } from "@nestjs/common"; import { Module } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service"; import { PrismaService } from "src/prisma/prisma.service";
import { BankCodesControllers } from "./controllers/bank-codes.controller"; import { BankCodesControllers } from "./controllers/bank-codes.controller";
import { BankCodesService } from "./services/bank-codes.services"; import { BankCodesService } from "./services/bank-codes.services";

View File

@ -2,10 +2,12 @@ import { PrismaService } from "src/prisma/prisma.service";
import { ExpensesController } from "./controllers/expenses.controller"; import { ExpensesController } from "./controllers/expenses.controller";
import { Module } from "@nestjs/common"; import { Module } from "@nestjs/common";
import { ExpensesService } from "./services/expenses.service"; import { ExpensesService } from "./services/expenses.service";
import { BusinessLogicsModule } from "src/business-logics/business-logics.module";
@Module({ @Module({
imports: [BusinessLogicsModule],
controllers: [ExpensesController], controllers: [ExpensesController],
providers: [ExpensesService, PrismaService], providers: [ExpensesService],
exports: [ ExpensesService ], exports: [ ExpensesService ],
}) })

View File

@ -3,48 +3,55 @@ import { PrismaService } from "src/prisma/prisma.service";
import { CreateExpenseDto } from "../dtos/create-expense"; import { CreateExpenseDto } from "../dtos/create-expense";
import { Expenses, ExpensesArchive } from "@prisma/client"; import { Expenses, ExpensesArchive } from "@prisma/client";
import { UpdateExpenseDto } from "../dtos/update-expense"; import { UpdateExpenseDto } from "../dtos/update-expense";
import { MileageService } from "src/business-logics/services/mileage.service";
@Injectable() @Injectable()
export class ExpensesService { export class ExpensesService {
constructor(private readonly prisma: PrismaService) {} constructor(
private readonly prisma: PrismaService,
private readonly mileageService: MileageService,
) {}
async create(dto: CreateExpenseDto): Promise<Expenses> { async create(dto: CreateExpenseDto): Promise<Expenses> {
const { timesheet_id, bank_code_id, date, amount, const { timesheet_id, bank_code_id, date, amount:rawAmount,
description, is_approved,supervisor_comment} = dto; 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: { //fetches type and modifier
timesheet: { const bankCode = await this.prisma.bankCodes.findUnique({
include: { where: { id: bank_code_id },
employee: { include: { user: true } }, select: { type: true, modifier: true },
},
},
bank_code: 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<Expenses[]> { findAll(): Promise<Expenses[]> {
return this.prisma.expenses.findMany({ return this.prisma.expenses.findMany({
include: { include: { timesheet: { include: { employee: { include: { user: true } } } } },
timesheet: {
include: {
employee: { include: { user: true } },
},
},
},
}); });
} }
async findOne(id: number): Promise<Expenses> { async findOne(id: number): Promise<Expenses> {
const expense = await this.prisma.expenses.findUnique({ const expense = await this.prisma.expenses.findUnique({
where: { id }, where: { id },
include: { include: { timesheet: { include: { employee: { include: { user:true } } } },
timesheet: {
include: {
employee: { include: { user:true } },
},
},
bank_code: true, bank_code: true,
}, },
}); });
@ -69,14 +76,7 @@ export class ExpensesService {
...(is_approved !== undefined && { is_approved }), ...(is_approved !== undefined && { is_approved }),
...(supervisor_comment !== undefined && { supervisor_comment }), ...(supervisor_comment !== undefined && { supervisor_comment }),
}, },
include: { include: { timesheet: { include: { employee: { include: { user: true } } } },
timesheet: {
include: {
employee: {
include: { user: true }
},
},
},
bank_code: true, bank_code: true,
}, },
}); });

View File

@ -1,21 +1,13 @@
import { PrismaService } from "src/prisma/prisma.service";
import { LeaveRequestController } from "./controllers/leave-requests.controller"; import { LeaveRequestController } from "./controllers/leave-requests.controller";
import { LeaveRequestsService } from "./services/leave-requests.service"; import { LeaveRequestsService } from "./services/leave-requests.service";
import { Module } from "@nestjs/common"; import { Module } from "@nestjs/common";
import { HolidayService } from "src/business-logic/holiday.service"; import { BusinessLogicsModule } from "src/business-logics/business-logics.module";
import { VacationService } from "src/business-logic/vacation.service";
import { SickLeaveService } from "src/business-logic/sick-leave.service";
@Module({ @Module({
imports: [BusinessLogicsModule],
controllers: [LeaveRequestController], controllers: [LeaveRequestController],
providers: [ providers: [LeaveRequestsService],
LeaveRequestsService, exports: [LeaveRequestsService],
PrismaService,
HolidayService,
VacationService,
SickLeaveService,
],
exports: [ LeaveRequestsService],
}) })
export class LeaveRequestsModule {} export class LeaveRequestsModule {}

View File

@ -3,9 +3,9 @@ import { PrismaService } from "src/prisma/prisma.service";
import { CreateLeaveRequestsDto } from "../dtos/create-leave-requests.dto"; import { CreateLeaveRequestsDto } from "../dtos/create-leave-requests.dto";
import { LeaveRequests, LeaveRequestsArchive } from "@prisma/client"; import { LeaveRequests, LeaveRequestsArchive } from "@prisma/client";
import { UpdateLeaveRequestsDto } from "../dtos/update-leave-requests.dto"; import { UpdateLeaveRequestsDto } from "../dtos/update-leave-requests.dto";
import { HolidayService } from "src/business-logic/holiday.service"; import { HolidayService } from "src/business-logics/services/holiday.service";
import { VacationService } from "src/business-logic/vacation.service"; import { SickLeaveService } from "src/business-logics/services/sick-leave.service";
import { SickLeaveService } from "src/business-logic/sick-leave.service"; import { VacationService } from "src/business-logics/services/vacation.service";
@Injectable() @Injectable()
export class LeaveRequestsService { export class LeaveRequestsService {

View File

@ -12,12 +12,7 @@ export class ShiftsService {
const { timesheet_id, bank_code_id, date, start_time, end_time } = dto; const { timesheet_id, bank_code_id, date, start_time, end_time } = dto;
return this.prisma.shifts.create({ return this.prisma.shifts.create({
data: { timesheet_id, bank_code_id, date, start_time, end_time }, data: { timesheet_id, bank_code_id, date, start_time, end_time },
include: { include: { timesheet: { include: { employee: { include: { user: true } } } },
timesheet: {
include: {
employee: { include: { user: true } },
},
},
bank_code: true, bank_code: true,
}, },
}); });
@ -25,29 +20,14 @@ export class ShiftsService {
findAll(): Promise<Shifts[]> { findAll(): Promise<Shifts[]> {
return this.prisma.shifts.findMany({ return this.prisma.shifts.findMany({
include: { include: { timesheet: { include: { employee: { include: { user:true } } } } },
timesheet: {
include: {
employee: {
include: { user:true }
},
},
},
},
}); });
} }
async findOne(id: number): Promise<Shifts> { async findOne(id: number): Promise<Shifts> {
const shift = await this.prisma.shifts.findUnique({ const shift = await this.prisma.shifts.findUnique({
where: { id }, where: { id },
include: { include: { timesheet: { include: { employee: { include: { user: true } } } },
timesheet: {
include: {
employee: {
include: { user: true }
},
},
},
bank_code: true, bank_code: true,
}, },
}); });
@ -69,14 +49,7 @@ export class ShiftsService {
...(start_time !== undefined && { start_time }), ...(start_time !== undefined && { start_time }),
...(end_time !== undefined && { end_time }), ...(end_time !== undefined && { end_time }),
}, },
include: { include: { timesheet: { include: { employee: { include: { user: true } } } },
timesheet: {
include: {
employee: {
include: { user: true }
},
},
},
bank_code: true, bank_code: true,
}, },
}); });

View File

@ -1,11 +1,12 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { ShiftsController } from './controllers/shifts.controller'; import { ShiftsController } from './controllers/shifts.controller';
import { ShiftsService } from './services/shifts.service'; import { ShiftsService } from './services/shifts.service';
import { PrismaService } from 'src/prisma/prisma.service'; import { BusinessLogicsModule } from 'src/business-logics/business-logics.module';
@Module({ @Module({
imports: [BusinessLogicsModule],
controllers: [ShiftsController], controllers: [ShiftsController],
providers: [ShiftsService, PrismaService], providers: [ShiftsService],
exports: [ShiftsService], exports: [ShiftsService],
}) })
export class ShiftsModule {} export class ShiftsModule {}

View File

@ -3,7 +3,7 @@ import { PrismaService } from 'src/prisma/prisma.service';
import { CreateTimesheetDto } from '../dtos/create-timesheet.dto'; import { CreateTimesheetDto } from '../dtos/create-timesheet.dto';
import { Timesheets, TimesheetsArchive } from '@prisma/client'; import { Timesheets, TimesheetsArchive } from '@prisma/client';
import { UpdateTimesheetDto } from '../dtos/update-timesheet.dto'; 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() @Injectable()
export class TimesheetsService { export class TimesheetsService {

View File

@ -1,14 +1,12 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TimesheetsController } from './controllers/timesheets.controller'; import { TimesheetsController } from './controllers/timesheets.controller';
import { TimesheetsService } from './services/timesheets.service'; import { TimesheetsService } from './services/timesheets.service';
import { OvertimeService } from 'src/business-logic/overtime.service'; import { BusinessLogicsModule } from 'src/business-logics/business-logics.module';
@Module({ @Module({
imports: [BusinessLogicsModule],
controllers: [TimesheetsController], controllers: [TimesheetsController],
providers: [ providers: [ TimesheetsService ],
TimesheetsService,
OvertimeService,
],
exports: [TimesheetsService], exports: [TimesheetsService],
}) })
export class TimesheetsModule {} export class TimesheetsModule {}