targo-backend/src/modules/shifts/services/shifts-query.service.ts

205 lines
8.1 KiB
TypeScript

import { Injectable, NotFoundException } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service";
import { NotificationsService } from "src/modules/notifications/services/notifications.service";
import { computeHours } from "src/common/utils/date-utils";
import { OverviewRow } from "../types and interfaces/shifts-overview-row.interface";
// const DAILY_LIMIT_HOURS = Number(process.env.DAILY_LIMIT_HOURS ?? 12);
@Injectable()
export class ShiftsQueryService {
constructor(
private readonly prisma: PrismaService,
private readonly notifs: NotificationsService,
) {}
async getSummary(period_id: number): Promise<OverviewRow[]> {
//fetch pay-period to display
const period = await this.prisma.payPeriods.findFirst({
where: { pay_period_no: period_id },
});
if(!period) {
throw new NotFoundException(`pay-period ${period_id} not found`);
}
const { period_start, period_end } = period;
//prepare shifts and expenses for display
const shifts = await this.prisma.shifts.findMany({
where: { date: { gte: period_start, lte: period_end } },
include: {
bank_code: true,
timesheet: { include: {
employee: { include: {
user:true,
supervisor: { include: { user: true } },
} },
} },
},
});
const expenses = await this.prisma.expenses.findMany({
where: { date: { gte: period_start, lte: period_end } },
include: {
bank_code: true,
timesheet: { include: { employee: {
include: { user:true,
supervisor: { include: { user:true } },
} },
} },
},
});
const mapRow = new Map<string, OverviewRow>();
for(const shift of shifts) {
const employeeId = shift.timesheet.employee.user_id;
const user = shift.timesheet.employee.user;
const sup = shift.timesheet.employee.supervisor?.user;
let row = mapRow.get(employeeId);
if(!row) {
row = {
full_name: `${user.first_name} ${user.last_name}`,
supervisor: sup? `${sup.first_name} ${sup.last_name }` : '',
total_regular_hrs: 0,
total_evening_hrs: 0,
total_overtime_hrs: 0,
total_expenses: 0,
total_mileage: 0,
is_approved: false,
};
}
const hours = computeHours(shift.start_time, shift.end_time);
switch(shift.bank_code.type) {
case 'regular' : row.total_regular_hrs += hours;
break;
case 'evening' : row.total_evening_hrs += hours;
break;
case 'overtime' : row.total_overtime_hrs += hours;
break;
default: row.total_regular_hrs += hours;
}
mapRow.set(employeeId, row);
}
for(const exp of expenses) {
const employee_id = exp.timesheet.employee.user_id;
const user = exp.timesheet.employee.user;
const sup = exp.timesheet.employee.supervisor?.user;
let row = mapRow.get(employee_id);
if(!row) {
row = {
full_name: `${user.first_name} ${user.last_name}`,
supervisor: sup? `${sup.first_name} ${sup.last_name }` : '',
total_regular_hrs: 0,
total_evening_hrs: 0,
total_overtime_hrs: 0,
total_expenses: 0,
total_mileage: 0,
is_approved: false,
};
}
const amount = Number(exp.amount);
row.total_expenses += amount;
if(exp.bank_code.type === 'mileage') {
row.total_mileage += amount;
}
mapRow.set(employee_id, row);
}
//return by default the list of employee in ascending alphabetical order
return Array.from(mapRow.values()).sort((a,b) => a.full_name.localeCompare(b.full_name));
}
//_____________________________________________________________________________________________
// Deprecated or unused methods
//_____________________________________________________________________________________________
// async update(id: number, dto: UpdateShiftsDto): Promise<Shifts> {
// await this.findOne(id);
// const { timesheet_id, bank_code_id, date,start_time,end_time, comment} = dto;
// return this.prisma.shifts.update({
// where: { id },
// data: {
// ...(timesheet_id !== undefined && { timesheet_id }),
// ...(bank_code_id !== undefined && { bank_code_id }),
// ...(date !== undefined && { date }),
// ...(start_time !== undefined && { start_time }),
// ...(end_time !== undefined && { end_time }),
// ...(comment !== undefined && { comment }),
// },
// include: { timesheet: { include: { employee: { include: { user: true } } } },
// bank_code: true,
// },
// });
// }
// async remove(id: number): Promise<Shifts> {
// await this.findOne(id);
// return this.prisma.shifts.delete({ where: { id } });
// }
// async create(dto: CreateShiftDto): Promise<Shifts> {
// const { timesheet_id, bank_code_id, date, start_time, end_time, comment } = dto;
// //shift creation
// const shift = await this.prisma.shifts.create({
// data: { timesheet_id, bank_code_id, date, start_time, end_time, comment },
// include: { timesheet: { include: { employee: { include: { user: true } } } },
// bank_code: true,
// },
// });
// //fetches all shifts of the same day to check for daily overtime
// const same_day_shifts = 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 total_hours = same_day_shifts.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(total_hours > DAILY_LIMIT_HOURS ) {
// const user_id = String(shift.timesheet.employee.user.id);
// const date_label = new Date(date).toLocaleDateString('fr-CA');
// this.notifs.notify(user_id, {
// type: 'shift.overtime.daily',
// severity: 'warn',
// message: `Tu viens de dépasser ${DAILY_LIMIT_HOURS.toFixed(2)}h pour la journée du ${date_label}
// (total: ${total_hours.toFixed(2)}h).`,
// ts: new Date().toISOString(),
// meta: {
// timesheet_id,
// date: new Date(date).toISOString(),
// total_hours,
// threshold: DAILY_LIMIT_HOURS,
// last_shift_id: shift.id
// },
// });
// }
// return shift;
// }
// async findAll(filters: SearchShiftsDto): Promise <Shifts[]> {
// const where = buildPrismaWhere(filters);
// const shifts = await this.prisma.shifts.findMany({ where })
// return shifts;
// }
// async findOne(id: number): Promise<Shifts> {
// const shift = await this.prisma.shifts.findUnique({
// where: { id },
// include: { timesheet: { include: { employee: { include: { user: true } } } },
// bank_code: true,
// },
// });
// if(!shift) {
// throw new NotFoundException(`Shift #${id} not found`);
// }
// return shift;
// }
}