fix(notify): fixes and setup notify for daily overtime
This commit is contained in:
parent
dc8c4d048c
commit
1e4ec836d3
|
|
@ -12,6 +12,7 @@ import { ExpensesModule } from './modules/expenses/expenses.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 { LeaveRequestsModule } from './modules/leave-requests/leave-requests.module';
|
import { LeaveRequestsModule } from './modules/leave-requests/leave-requests.module';
|
||||||
|
import { NotificationsModule } from './modules/notifications/notifications.module';
|
||||||
import { OauthSessionsModule } from './modules/oauth-sessions/oauth-sessions.module';
|
import { OauthSessionsModule } from './modules/oauth-sessions/oauth-sessions.module';
|
||||||
import { OvertimeService } from './modules/business-logics/services/overtime.service';
|
import { OvertimeService } from './modules/business-logics/services/overtime.service';
|
||||||
import { PayperiodsModule } from './modules/pay-periods/pay-periods.module';
|
import { PayperiodsModule } from './modules/pay-periods/pay-periods.module';
|
||||||
|
|
@ -33,6 +34,7 @@ import { UsersModule } from './modules/users-management/users.module';
|
||||||
ExpensesModule,
|
ExpensesModule,
|
||||||
HealthModule,
|
HealthModule,
|
||||||
LeaveRequestsModule,
|
LeaveRequestsModule,
|
||||||
|
NotificationsModule,
|
||||||
OauthSessionsModule,
|
OauthSessionsModule,
|
||||||
PayperiodsModule,
|
PayperiodsModule,
|
||||||
PrismaModule,
|
PrismaModule,
|
||||||
|
|
|
||||||
|
|
@ -48,3 +48,37 @@ export function getWeekEnd(startOfWeek: Date): Date {
|
||||||
export function getYearStart(date:Date): Date {
|
export function getYearStart(date:Date): Date {
|
||||||
return new Date(date.getFullYear(),0,1,0,0,0,0);
|
return new Date(date.getFullYear(),0,1,0,0,0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//cloning methods (helps with notify for overtime in a single day)
|
||||||
|
// export function toDateOnly(day: Date): Date {
|
||||||
|
// const d = new Date(day);
|
||||||
|
// d.setHours(0, 0, 0, 0);
|
||||||
|
// return d;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function composeDateWithTime(day: Date, timeOnly: Date): Date {
|
||||||
|
// const base = toDateOnly(day);
|
||||||
|
// base.setHours(timeOnly.getHours(),
|
||||||
|
// timeOnly.getMinutes(),
|
||||||
|
// timeOnly.getSeconds(),
|
||||||
|
// timeOnly.getMilliseconds());
|
||||||
|
// return base;
|
||||||
|
// }
|
||||||
|
|
||||||
|
export function hoursBetweenSameDay(day: Date, startTime: Date, endTime: Date): number {
|
||||||
|
const start = new Date(day); start.setHours(startTime.getHours(),
|
||||||
|
startTime.getMinutes(),
|
||||||
|
startTime.getSeconds(),
|
||||||
|
startTime.getMilliseconds());
|
||||||
|
const end = new Date(day); end.setHours(endTime.getHours(),
|
||||||
|
endTime.getMinutes(),
|
||||||
|
endTime.getSeconds(),
|
||||||
|
endTime.getMilliseconds());
|
||||||
|
const ms = Math.max(0, end.getTime() - start.getTime());
|
||||||
|
return ms / 3_600_000; // decimal hours
|
||||||
|
}
|
||||||
|
|
||||||
|
// //utils to print Date into locale date format
|
||||||
|
// export function fmtDayLocal(day:Date, locale = 'fr-CA'): string {
|
||||||
|
// return new Date(day).toLocaleDateString(locale, { year: 'numeric', month: '2-digit', day: '2-digit'});
|
||||||
|
// }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
export const NOTIF_TYPES = {
|
||||||
|
SHIFT_OVERTIME_DAILY: 'shift.overtime.daily',
|
||||||
|
|
||||||
|
} as const;
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Controller, Get, Req, Sse, UseGuards,
|
import { Controller, Get, Req, Sse, UseGuards,
|
||||||
MessageEvent as NestMessageEvent } from "@nestjs/common";
|
MessageEvent as NestMessageEvent } from "@nestjs/common";
|
||||||
import { JwtAuthGuard } from "../authentication/guards/jwt-auth.guard";
|
import { JwtAuthGuard } from "../../authentication/guards/jwt-auth.guard";
|
||||||
import { NotificationsService } from "./notifications.service";
|
import { NotificationsService } from "../services/notifications.service";
|
||||||
import { Observable } from "rxjs";
|
import { Observable } from "rxjs";
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { NotificationsController } from "./notifications.controller";
|
import { NotificationsController } from "./controllers/notifications.controller";
|
||||||
import { NotificationsService } from "./notifications.service";
|
import { NotificationsService } from "./services/notifications.service";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
providers: [NotificationsService],
|
providers: [NotificationsService],
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Injectable, Logger } from "@nestjs/common";
|
import { Injectable, Logger } from "@nestjs/common";
|
||||||
import { Subject } from "rxjs";
|
import { Subject } from "rxjs";
|
||||||
import { NotificationCard } from "./notifications.types";
|
import { NotificationCard } from "../dtos/notifications.types";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class NotificationsService {
|
export class NotificationsService {
|
||||||
|
|
@ -5,19 +5,59 @@ import { Shifts, ShiftsArchive } from "@prisma/client";
|
||||||
import { UpdateShiftsDto } from "../dtos/update-shift.dto";
|
import { UpdateShiftsDto } from "../dtos/update-shift.dto";
|
||||||
import { buildPrismaWhere } from "src/common/shared/build-prisma-where";
|
import { buildPrismaWhere } from "src/common/shared/build-prisma-where";
|
||||||
import { SearchShiftsDto } from "../dtos/search-shifts.dto";
|
import { SearchShiftsDto } from "../dtos/search-shifts.dto";
|
||||||
|
import { NotificationsService } from "src/modules/notifications/services/notifications.service";
|
||||||
|
import { hoursBetweenSameDay } from "src/common/utils/date-utils";
|
||||||
|
|
||||||
|
const DAILY_LIMIT_HOURS = Number(process.env.DAILY_LIMIT_HOURS ?? 8);
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ShiftsService {
|
export class ShiftsService {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly notifs: NotificationsService,
|
||||||
|
) {}
|
||||||
|
|
||||||
async create(dto: CreateShiftDto): Promise<Shifts> {
|
async create(dto: CreateShiftDto): Promise<Shifts> {
|
||||||
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({
|
|
||||||
|
//shift creation
|
||||||
|
const shift = await 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: { timesheet: { include: { employee: { include: { user: true } } } },
|
include: { timesheet: { include: { employee: { include: { user: true } } } },
|
||||||
bank_code: true,
|
bank_code: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//fetches all shifts of the same day to check for daily overtime
|
||||||
|
const sameDayShifts = await this.prisma.shifts.findMany({
|
||||||
|
where: { timesheet_id, date },
|
||||||
|
select: { id: true, date: true, start_time: true, end_time: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
//sums hours of the day
|
||||||
|
const totalHours = sameDayShifts.reduce((sum, s) => {
|
||||||
|
return sum + hoursBetweenSameDay(s.date, s.start_time, s.end_time);
|
||||||
|
}, 0 );
|
||||||
|
|
||||||
|
//Notify if total hours > 8 for a single day
|
||||||
|
if(totalHours > DAILY_LIMIT_HOURS ) {
|
||||||
|
const userId = String(shift.timesheet.employee.user.id);
|
||||||
|
const dateLabel = new Date(date).toLocaleDateString('fr-CA');
|
||||||
|
this.notifs.notify(userId, {
|
||||||
|
type: 'shift.overtime.daily',
|
||||||
|
severity: 'warn',
|
||||||
|
message: `Tu viens de dépasser ${DAILY_LIMIT_HOURS.toFixed(2)}h pour la journée du ${dateLabel} (total: ${totalHours.toFixed(2)}h).`,
|
||||||
|
ts: new Date().toISOString(),
|
||||||
|
meta: {
|
||||||
|
timesheet_id,
|
||||||
|
date: new Date(date).toISOString(),
|
||||||
|
totalHours,
|
||||||
|
threshold: DAILY_LIMIT_HOURS,
|
||||||
|
lastShiftId: shift.id
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAll(filters: SearchShiftsDto): Promise <Shifts[]> {
|
async findAll(filters: SearchShiftsDto): Promise <Shifts[]> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user