import { Injectable, NotFoundException } from '@nestjs/common'; 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'; @Injectable() export class TimesheetsService { constructor( private readonly prisma: PrismaService, private readonly overtime: OvertimeService, ) {} async create(dto : CreateTimesheetDto): Promise { const { employee_id, is_approved } = dto; return this.prisma.timesheets.create({ data: { employee_id, is_approved: is_approved ?? false }, include: { employee: { include: { user: true } }, }, }); } async findAll(): Promise { const list = await this.prisma.timesheets.findMany({ include: { shift: { include: { bank_code: true } }, expense: { include: { bank_code: true } }, employee: { include: { user : true } }, }, }); 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 regularHours = Math.min(8, hours); const dailyOvertime = this.overtime.getDailyOvertimeHours(s.start_time, s.end_time); const payRegular = regularHours * s.bank_code.modifier; const payOvertime = this.overtime.calculateOvertimePay(dailyOvertime, s.bank_code.modifier); return { ...s, hours, payRegular, payOvertime }; }); const weeklyOvertimeHours = detailedShifts.length ? await this.overtime.getWeeklyOvertimeHours( timesheet.employee_id, timesheet.shift[0].date): 0; return { ...timesheet, shift: detailedShifts, weeklyOvertimeHours }; }) ); } async findOne(id: number): Promise { const timesheet = await this.prisma.timesheets.findUnique({ where: { id }, include: { shift: { include: { bank_code: true } }, expense: { include: { bank_code: true } }, employee: { include: { user: true } }, }, }); if(!timesheet) { throw new NotFoundException(`Timesheet #${id} not found`); } const detailedShifts = timesheet.shift.map( s => { const hours = this.overtime.computedHours(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; const payOvertime = this.overtime.calculateOvertimePay(dailyOvertime, s.bank_code.modifier); return { ...s, hours, payRegular, payOvertime }; }); const weeklyOvertimeHours = detailedShifts.length ? await this.overtime.getWeeklyOvertimeHours( timesheet.employee_id, timesheet.shift[0].date): 0; return { ...timesheet, shift: detailedShifts, weeklyOvertimeHours }; } async update(id: number, dto:UpdateTimesheetDto): Promise { await this.findOne(id); const { employee_id, is_approved } = dto; return this.prisma.timesheets.update({ where: { id }, data: { ...(employee_id !== undefined && { employee_id }), ...(is_approved !== undefined && { is_approved }), }, include: { employee: { include: { user: true } }, }, }); } async remove(id: number): Promise { await this.findOne(id); return this.prisma.timesheets.delete({ where: { id } }); } //archivation functions ****************************************************** async archiveOld(): Promise { //calcul du cutoff pour archivation const cutoff = new Date(); cutoff.setMonth(cutoff.getMonth() - 6) await this.prisma.$transaction(async transaction => { //fetches all timesheets to cutoff const oldSheets = await transaction.timesheets.findMany({ where: { shift: { every: { date: { lt: cutoff } } }, }, select: { id: true, employee_id: true, is_approved: true, }, }); if( oldSheets.length === 0) { return; } //preping data for archivation const archiveDate = oldSheets.map(sheet => ({ timesheet_id: sheet.id, employee_id: sheet.employee_id, is_approved: sheet.is_approved, })); //copying data from timesheets table to archive table await transaction.timesheetsArchive.createMany({ data: archiveDate }); //removing data from timesheets table await transaction.timesheets.deleteMany({ where: { id: { in: oldSheets.map(s => s.id) } } }); }); } //fetches all archived timesheets async findAllArchived(): Promise { return this.prisma.timesheetsArchive.findMany(); } //fetches an archived timesheet async findOneArchived(id: number): Promise { return this.prisma.timesheetsArchive.findUniqueOrThrow({ where: { id } }); } }