import { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/common/utils/constants.utils"; import { Injectable } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; import { Timesheet, Timesheets } from "src/time-and-attendance/timesheets/timesheet.dto"; import { Result } from "src/common/errors/result-error.factory"; import { Prisma } from "@prisma/client"; import { toDateFromString, sevenDaysFrom, toStringFromDate, toHHmmFromDate } from "src/common/utils/date-utils"; import { mapOneTimesheet } from "src/time-and-attendance/timesheets/timesheet.mapper"; @Injectable() export class GetTimesheetsOverviewService { constructor( private readonly prisma: PrismaService, private readonly emailResolver: EmailToIdResolver, ) { } //----------------------------------------------------------------------------------- // GET TIMESHEETS FOR A SELECTED EMPLOYEE //----------------------------------------------------------------------------------- async getTimesheetsForEmployeeByPeriod(email: string, pay_year: number, pay_period_no: number, employee_email?: string): Promise> { try { const account_email = employee_email ?? email; //find period using year and period_no const period = await this.prisma.payPeriods.findFirst({ where: { pay_year, pay_period_no } }); if (!period) return { success: false, error: `PAY_PERIOD_NOT_FOUND` }; //fetch the employee_id using the email const employee_id = await this.emailResolver.findIdByEmail(account_email); if (!employee_id.success) return { success: false, error: employee_id.error } //loads the timesheets related to the fetched pay-period let rows = await this.loadTimesheets(employee_id.data, period.period_start, period.period_end); //Normalized dates from pay-period strings const normalized_start = toDateFromString(period.period_start); const normalized_end = toDateFromString(period.period_end); //creates empty timesheet to make sure to return desired amount of timesheet for (let i = 0; i < NUMBER_OF_TIMESHEETS_TO_RETURN; i++) { const week_start = new Date(normalized_start); week_start.setUTCDate(week_start.getUTCDate() + i * 7); if (week_start.getTime() > normalized_end.getTime()) break; const has_existing_timesheets = rows.some( (row) => toDateFromString(row.start_date).getTime() === week_start.getTime() ); if (!has_existing_timesheets) await this.ensureTimesheet(employee_id.data, week_start); } rows = await this.loadTimesheets(employee_id.data, period.period_start, period.period_end); //find user infos using the employee_id const employee = await this.prisma.employees.findUnique({ where: { id: employee_id.data }, include: { schedule_preset: true, user: true }, }); if (!employee) return { success: false, error: `EMPLOYEE_NOT_FOUND` } //builds employee details const has_preset_schedule = employee.schedule_preset !== null; const user = employee.user; const employee_fullname = `${user.first_name} ${user.last_name}`.trim(); //maps all timesheet's infos const timesheets = await Promise.all(rows.map((timesheet) => mapOneTimesheet(timesheet))); if (!timesheets) return { success: false, error: 'INVALID_TIMESHEET' } return { success: true, data: { has_preset_schedule, employee_fullname, timesheets } }; } catch (error) { return { success: false, error: 'TIMESHEET_NOT_FOUND' } } } //----------------------------------------------------------------------------------- // MAPPERS & HELPERS //----------------------------------------------------------------------------------- //fetch timesheet's infos private async loadTimesheets(employee_id: number, period_start: Date, period_end: Date) { return this.prisma.timesheets.findMany({ where: { employee_id, start_date: { gte: period_start, lte: period_end } }, include: { employee: { include: { user: true } }, shift: { include: { bank_code: true }, orderBy: { start_time: 'asc' } }, expense: { include: { bank_code: true, attachment_record: true }, orderBy: [{ date: 'asc' }, { bank_code_id: 'desc' }] }, }, orderBy: { start_date: 'asc' }, }); } private ensureTimesheet = async (employee_id: number, start_date: Date | string) => { const start = toDateFromString(start_date); let row = await this.prisma.timesheets.findFirst({ where: { employee_id, start_date: start }, include: { employee: { include: { user: true } }, shift: { include: { bank_code: true } }, expense: { include: { bank_code: true, attachment_record: true } }, }, }); if (row) return row; await this.prisma.timesheets.create({ data: { employee_id, start_date: start, is_approved: false }, }); row = await this.prisma.timesheets.findFirst({ where: { employee_id, start_date: start }, include: { employee: { include: { user: true } }, shift: { include: { bank_code: true } }, expense: { include: { bank_code: true, attachment_record: true, } }, }, }); return row!; } }