Merge branch 'main' of git.targo.ca:Targo/targo_backend into dev/setup/modules/MatthieuH
This commit is contained in:
commit
ef5af80471
|
|
@ -545,6 +545,9 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
<<<<<<< HEAD
|
||||
"/auth/v1/login": {
|
||||
=======
|
||||
"/Expenses": {
|
||||
"post": {
|
||||
"operationId": "ExpensesController_create",
|
||||
|
|
@ -1142,6 +1145,7 @@
|
|||
}
|
||||
},
|
||||
"/auth/login": {
|
||||
>>>>>>> b0406b3a4c00223b9430ef29b60a4775beca4328
|
||||
"get": {
|
||||
"operationId": "AuthController_login",
|
||||
"parameters": [],
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import { BankCodesModule } from './modules/bank-codes/bank-codes.module';
|
|||
import { OvertimeService } from './modules/business-logics/services/overtime.service';
|
||||
import { BusinessLogicsModule } from './modules/business-logics/business-logics.module';
|
||||
import { OauthSessionsModule } from './modules/oauth-sessions/oauth-sessions.module';
|
||||
import { CsvExportModule } from './modules/exports/csv-exports.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
@ -27,6 +28,7 @@ import { OauthSessionsModule } from './modules/oauth-sessions/oauth-sessions.mod
|
|||
AuthenticationModule,
|
||||
BankCodesModule,
|
||||
BusinessLogicsModule,
|
||||
CsvExportModule,
|
||||
CustomersModule,
|
||||
EmployeesModule,
|
||||
ExpensesModule,
|
||||
|
|
|
|||
50
src/common/utils/date-utils.ts
Normal file
50
src/common/utils/date-utils.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
//lenght of a shift, rouded to nearest 'x' minute
|
||||
export function computeHours(start: Date, end: Date, roundToMinutes?: number): number {
|
||||
const diffMs = end.getTime() - start.getTime();
|
||||
const totalMinutes = diffMs / 60000;
|
||||
const minutes = roundToMinutes ?
|
||||
Math.round(totalMinutes / roundToMinutes) * roundToMinutes :
|
||||
totalMinutes;
|
||||
return +(minutes / 60).toFixed(2);
|
||||
}
|
||||
|
||||
//round the amount of hours to quarter
|
||||
export function roundToQuarterHour(hours: number): number {
|
||||
return Math.round(hours *4) / 4;
|
||||
}
|
||||
|
||||
//calculate the number of the week (1 or 2)
|
||||
export function computeWeekNumber(periodStart: Date, targetDate: Date): number {
|
||||
const days = Math.floor( targetDate.getTime() - periodStart.getTime()) /
|
||||
(1000 * 60 * 60 * 24);
|
||||
return Math.floor(days / 7) +1;
|
||||
}
|
||||
|
||||
//Date format YYY-MM-DD
|
||||
export function formatDateISO(d:Date): string {
|
||||
return d.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
//fetch firts day of the week (Sunday)
|
||||
export function getWeekStart(date:Date, firstDayOfWeek = 0): Date {
|
||||
const d = new Date(date);
|
||||
const day = d.getDay();
|
||||
const diff = (day < firstDayOfWeek ? 7 : 0) + (day - firstDayOfWeek);
|
||||
d.setDate(d.getDate() - diff);
|
||||
d.setHours(0,0,0,0);
|
||||
return d;
|
||||
}
|
||||
|
||||
//fetch last day of the week (Saturday)
|
||||
export function getWeekEnd(startOfWeek: Date): Date {
|
||||
const d = new Date(startOfWeek);
|
||||
d.setDate(d.getDate() + 6);
|
||||
d.setHours(23,59,59,999);
|
||||
return d;
|
||||
}
|
||||
|
||||
//returns january 1st of the selected date's year
|
||||
export function getYearStart(date:Date): Date {
|
||||
return new Date(date.getFullYear(),0,1,0,0,0,0);
|
||||
}
|
||||
|
|
@ -36,12 +36,18 @@ async function bootstrap() {
|
|||
rolling: true,
|
||||
cookie: {
|
||||
maxAge: 30 * 60 * 1000,
|
||||
httpOnly: false,
|
||||
httpOnly: true,
|
||||
}
|
||||
}))
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
|
||||
// Enable CORS
|
||||
app.enableCors({
|
||||
origin: 'http://localhost:9000',
|
||||
credentials: true,
|
||||
});
|
||||
|
||||
//swagger config
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Targo_Backend')
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ import { Request, Response } from 'express';
|
|||
export class AuthController {
|
||||
|
||||
@UseGuards(OIDCLoginGuard)
|
||||
@Get('/login')
|
||||
@Get('/v1/login')
|
||||
login() {}
|
||||
|
||||
@Get('/callback')
|
||||
@UseGuards(OIDCLoginGuard)
|
||||
loginCallback(@Req() req: Request, @Res() res: Response) {
|
||||
res.redirect('/');
|
||||
res.redirect('http://localhost:9000/#/login-success');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ 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
|
||||
//THIS SERVICE IS NOT USED, RULES TO BE DETERMINED WITH MIKE/HR/ACCOUNTING
|
||||
@Injectable()
|
||||
export class AfterHoursService {
|
||||
private readonly logger = new Logger(AfterHoursService.name);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { PrismaService } from "../../../prisma/prisma.service";
|
||||
import { computeHours, getWeekStart } from "src/common/utils/date-utils";
|
||||
|
||||
@Injectable()
|
||||
export class HolidayService {
|
||||
|
|
@ -7,32 +8,15 @@ export class HolidayService {
|
|||
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
//return the sunday of the current week that includes the holiday
|
||||
private getWeekStart(date: Date): Date {
|
||||
const day = new Date(date);
|
||||
const offset = day.getDay();
|
||||
day.setDate(day.getDate() - offset);
|
||||
day.setHours(0,0,0,0);
|
||||
return day;
|
||||
}
|
||||
|
||||
//rounds minutes to 5s
|
||||
private computeHours(start: Date, end: Date): number {
|
||||
const durationMS = end.getTime() - start.getTime();
|
||||
const totalMinutes = durationMS / 60000;
|
||||
const rounded = Math.round(totalMinutes / 5) * 5;
|
||||
return rounded / 60;
|
||||
}
|
||||
|
||||
private async computeHoursPrevious4Weeks(employeeId: number, holidayDate: Date): Promise<number> {
|
||||
//sets the end of the window to 1ms before the week with the holiday
|
||||
const holidayWeekStart = this.getWeekStart(holidayDate);
|
||||
const holidayWeekStart = getWeekStart(holidayDate);
|
||||
const windowEnd = new Date(holidayWeekStart.getTime() - 1);
|
||||
//sets the start of the window to 28 days ( 4 completed weeks ) before the week with the holiday
|
||||
const windowStart = new Date(windowEnd.getTime() - 28 * 24 * 60 * 60000 + 1 )
|
||||
|
||||
const validCodes = ['G1', 'G45', 'G56', 'G104', 'G105', 'G700'];
|
||||
//fetches all shift of the employee in said window ( 4 completed weeks )
|
||||
//fetches all shift of the employee in said window ( 4 previous completed weeks )
|
||||
const shifts = await this.prisma.shifts.findMany({
|
||||
where: { timesheet: { employee_id: employeeId } ,
|
||||
date: { gte: windowStart, lte: windowEnd },
|
||||
|
|
@ -41,7 +25,7 @@ export class HolidayService {
|
|||
select: { date: true, start_time: true, end_time: true },
|
||||
});
|
||||
|
||||
const totalHours = shifts.map(s => this.computeHours(s.start_time, s.end_time)).reduce((sum, h)=> sum + h, 0);
|
||||
const totalHours = shifts.map(s => computeHours(s.start_time, s.end_time)).reduce((sum, h)=> sum + h, 0);
|
||||
const dailyHours = totalHours / 20;
|
||||
|
||||
return dailyHours;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { PrismaService } from '../../../prisma/prisma.service';
|
||||
import { getWeekStart, getWeekEnd, computeHours } from 'src/common/utils/date-utils';
|
||||
|
||||
@Injectable()
|
||||
export class OvertimeService {
|
||||
|
|
@ -10,47 +11,18 @@ export class OvertimeService {
|
|||
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
// calculate decimal hours rounded to nearest 5 min
|
||||
computedHours(start: Date, end: Date): number {
|
||||
const durationMs = end.getTime() - start.getTime();
|
||||
const totalMinutes = durationMs / 60000;
|
||||
|
||||
//rounded to 5 min
|
||||
const rounded = Math.round(totalMinutes / 5) * 5;
|
||||
const hours = rounded / 60;
|
||||
this.logger.debug(`computedHours: raw=${totalMinutes.toFixed(1)}min rounded = ${rounded}min (${hours.toFixed(2)}h)`);
|
||||
return hours;
|
||||
}
|
||||
|
||||
//calculate Daily overtime
|
||||
getDailyOvertimeHours(start: Date, end: Date): number {
|
||||
const hours = this.computedHours(start, end);
|
||||
const hours = computeHours(start, end, 5);
|
||||
const overtime = Math.max(0, hours - this.dailyMax);
|
||||
this.logger.debug(`getDailyOvertimeHours : ${overtime.toFixed(2)}h (threshold ${this.dailyMax})`);
|
||||
return overtime;
|
||||
}
|
||||
|
||||
//sets first day of the week to be sunday
|
||||
private getWeekStart(date:Date): Date {
|
||||
const d = new Date(date);
|
||||
const day = d.getDay(); // return sunday = 0, monday = 1, etc
|
||||
d.setDate(d.getDate() - day);
|
||||
d.setHours(0,0,0,0,); // puts start of the week at sunday morning at 00:00
|
||||
return d;
|
||||
}
|
||||
|
||||
//sets last day of the week to be saturday
|
||||
private getWeekEnd(startDate:Date): Date {
|
||||
const d = new Date(startDate);
|
||||
d.setDate(d.getDate() +6); //sets last day to be saturday
|
||||
d.setHours(23,59,59,999); //puts end of the week at saturday night at 00:00 minus 1ms
|
||||
return d;
|
||||
}
|
||||
|
||||
//calculate Weekly overtime
|
||||
async getWeeklyOvertimeHours(employeeId: number, refDate: Date): Promise<number> {
|
||||
const weekStart = this.getWeekStart(refDate);
|
||||
const weekEnd = this.getWeekEnd(weekStart);
|
||||
const weekStart = getWeekStart(refDate);
|
||||
const weekEnd = getWeekEnd(weekStart);
|
||||
|
||||
//fetches all shifts containing hours
|
||||
const shifts = await this.prisma.shifts.findMany({
|
||||
|
|
@ -63,7 +35,7 @@ export class OvertimeService {
|
|||
});
|
||||
|
||||
//calculate total hours of those shifts minus weekly Max to find total overtime hours
|
||||
const total = shifts.map(shift => this.computedHours(shift.start_time, shift.end_time))
|
||||
const total = shifts.map(shift => computeHours(shift.start_time, shift.end_time, 5))
|
||||
.reduce((sum, hours)=> sum+hours, 0);
|
||||
const overtime = Math.max(0, total - this.weeklyMax);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { PrismaService } from "../../../prisma/prisma.service";
|
||||
import { getYearStart, roundToQuarterHour } from "src/common/utils/date-utils";
|
||||
|
||||
@Injectable()
|
||||
export class SickLeaveService {
|
||||
|
|
@ -7,10 +8,10 @@ export class SickLeaveService {
|
|||
|
||||
private readonly logger = new Logger(SickLeaveService.name);
|
||||
|
||||
async calculateSickLeavePay(employeeId: number, startDate: Date, daysRequested: number, modifier: number): Promise<number> {
|
||||
async calculateSickLeavePay(employeeId: number, referenceDate: Date, daysRequested: number, modifier: number): Promise<number> {
|
||||
//sets the year to jan 1st to dec 31st
|
||||
const periodStart = new Date(startDate.getFullYear(), 0, 1);
|
||||
const periodEnd = startDate;
|
||||
const periodStart = getYearStart(referenceDate);
|
||||
const periodEnd = referenceDate;
|
||||
|
||||
//fetches all shifts of a selected employee
|
||||
const shifts = await this.prisma.shifts.findMany({
|
||||
|
|
@ -54,7 +55,7 @@ export class SickLeaveService {
|
|||
|
||||
const payableDays = Math.min(acquiredDays, daysRequested);
|
||||
const rawHours = payableDays * 8 * modifier;
|
||||
const rounded = Math.round(rawHours * 4) / 4;
|
||||
const rounded = roundToQuarterHour(rawHours)
|
||||
this.logger.debug(`Sick leave pay: days= ${payableDays}, modifier= ${modifier}, hours= ${rounded}`);
|
||||
return rounded;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Injectable, NotFoundException } from "@nestjs/common";
|
|||
import { EmployeePeriodOverviewDto } from "../dtos/overview-employee-period.dto";
|
||||
import { PayPeriodOverviewDto } from "../dtos/overview-pay-period.dto";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { computeHours } from "src/common/utils/date-utils";
|
||||
|
||||
@Injectable()
|
||||
export class PayPeriodsOverviewService {
|
||||
|
|
@ -40,7 +41,7 @@ export class PayPeriodsOverviewService {
|
|||
const user = employee_record.user;
|
||||
const employee_id = employee_record.user_id;
|
||||
const employee_name = `${user.first_name} ${user.last_name}`;
|
||||
const hours = (shift.end_time.getTime() - shift.start_time.getTime() / 3600000);
|
||||
const hours = computeHours(shift.start_time, shift.end_time);
|
||||
|
||||
//check if employee had prior shifts and adds hours of found shift to the total hours
|
||||
if (map.has(employee_id)) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { computeHours } from "src/common/utils/date-utils";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
|
||||
export interface OverviewRow {
|
||||
|
|
@ -16,13 +17,7 @@ export interface OverviewRow {
|
|||
export class ShiftsOverviewService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
private computeHours(start: Date, end: Date): number {
|
||||
const diffMs = end.getTime() - start.getTime();
|
||||
const hours = diffMs / 1000 / 3600;
|
||||
return parseFloat(hours.toFixed(2));
|
||||
}
|
||||
|
||||
async getSummary(periodId: number): Promise<OverviewRow[]> {
|
||||
async getSummary(periodId: number): Promise<ValidationRow[]> {
|
||||
//fetch pay-period to display
|
||||
const period = await this.prisma.payPeriods.findUnique({
|
||||
where: { period_number: periodId },
|
||||
|
|
@ -77,7 +72,7 @@ export class ShiftsOverviewService {
|
|||
isValidated: false,
|
||||
};
|
||||
}
|
||||
const hours = this.computeHours(s.start_time, s.end_time);
|
||||
const hours = computeHours(s.start_time, s.end_time);
|
||||
|
||||
switch(s.bank_code.type) {
|
||||
case 'regular' : row.totalRegularHrs += hours;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { CreateTimesheetDto } from '../dtos/create-timesheet.dto';
|
|||
import { Timesheets, TimesheetsArchive } from '@prisma/client';
|
||||
import { UpdateTimesheetDto } from '../dtos/update-timesheet.dto';
|
||||
import { OvertimeService } from 'src/modules/business-logics/services/overtime.service';
|
||||
import { computeHours } from 'src/common/utils/date-utils';
|
||||
|
||||
@Injectable()
|
||||
export class TimesheetsService {
|
||||
|
|
@ -35,7 +36,7 @@ export class TimesheetsService {
|
|||
return Promise.all(
|
||||
list.map(async timesheet => {
|
||||
const detailedShifts = timesheet.shift.map(s => {
|
||||
const hours = this.overtime.computedHours(s.start_time, s.end_time);
|
||||
const hours = computeHours(s.start_time, s.end_time,5);
|
||||
const regularHours = Math.min(8, hours);
|
||||
const dailyOvertime = this.overtime.getDailyOvertimeHours(s.start_time, s.end_time);
|
||||
const payRegular = regularHours * s.bank_code.modifier;
|
||||
|
|
@ -65,7 +66,7 @@ export class TimesheetsService {
|
|||
}
|
||||
|
||||
const detailedShifts = timesheet.shift.map( s => {
|
||||
const hours = this.overtime.computedHours(s.start_time, s.end_time);
|
||||
const hours = computeHours(s.start_time, s.end_time);
|
||||
const regularHours = Math.min(8, hours);
|
||||
const dailyOvertime = this.overtime.getDailyOvertimeHours(s.start_time, s.end_time);
|
||||
const payRegular = regularHours * s.bank_code.modifier;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user