diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 87d2a87..fe774db 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -104,6 +104,14 @@ "schema": { "type": "number" } + }, + { + "name": "employee_email", + "required": true, + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { diff --git a/src/common/utils/date-utils.ts b/src/common/utils/date-utils.ts index 604da75..60afab9 100644 --- a/src/common/utils/date-utils.ts +++ b/src/common/utils/date-utils.ts @@ -57,6 +57,7 @@ import { ANCHOR_ISO, MS_PER_DAY, PERIODS_PER_YEAR, PERIOD_DAYS } from "src/commo //ensures the week starts from sunday export function weekStartSunday(date_local: Date): Date { const start_date = new Date(); + start_date.setUTCFullYear(date_local.getUTCFullYear(), date_local.getUTCMonth()); start_date.setDate(date_local.getUTCDate() - date_local.getUTCDay()); return start_date; } diff --git a/src/time-and-attendance/pay-period/services/pay-periods-query.service.ts b/src/time-and-attendance/pay-period/services/pay-periods-query.service.ts index 59d4f3f..8978651 100644 --- a/src/time-and-attendance/pay-period/services/pay-periods-query.service.ts +++ b/src/time-and-attendance/pay-period/services/pay-periods-query.service.ts @@ -6,6 +6,7 @@ import { EmployeePeriodOverviewDto } from "../dtos/overview-employee-period.dto" import { PayPeriodDto } from "../dtos/pay-period.dto"; import { mapPayPeriodToDto } from "../mappers/pay-periods.mapper"; import { Result } from "src/common/errors/result-error.factory"; + @Injectable() export class PayPeriodsQueryService { constructor(private readonly prisma: PrismaService) { } @@ -151,14 +152,17 @@ export class PayPeriodsQueryService { : new Date(`${period.payday}T00:00:00.000Z`); //restrictEmployeeIds = filter for shifts and expenses by employees - const where_employee = options?.filtered_employee_ids?.length ? { employee_id: { in: options.filtered_employee_ids } } : {}; + const where_employee = options?.filtered_employee_ids?.length ? + { + date: { gte: start, lte: end }, + timesheet: { employee_id: { in: options.filtered_employee_ids } }, + } + : + { date: { gte: start, lte: end } }; // SHIFTS (filtered by crew) const shifts = await this.prisma.shifts.findMany({ - where: { - date: { gte: start, lte: end }, - timesheet: where_employee, - }, + where: where_employee, select: { start_time: true, end_time: true, @@ -186,10 +190,7 @@ export class PayPeriodsQueryService { // EXPENSES (filtered by crew) const expenses = await this.prisma.expenses.findMany({ - where: { - date: { gte: start, lte: end }, - timesheet: where_employee, - }, + where: where_employee, select: { amount: true, timesheet: { @@ -237,6 +238,39 @@ export class PayPeriodsQueryService { is_remote: true, }); } + } else { + const all_employees = await this.prisma.employees.findMany({ + include: { + user: { + select: { + first_name: true, + last_name: true, + email: true + } + } + }, + }); + + for (const employee of all_employees) { + by_employee.set(employee.id, { + email: employee.user.email, + employee_name: employee.user.first_name + ' ' + employee.user.last_name, + regular_hours: 0, + other_hours: { + evening_hours: 0, + emergency_hours: 0, + overtime_hours: 0, + sick_hours: 0, + holiday_hours: 0, + vacation_hours: 0, + }, + total_hours: 0, + expenses: 0, + mileage: 0, + is_approved: true, + is_remote: true, + }); + } } const ensure = (id: number, name: string, email: string) => { diff --git a/src/time-and-attendance/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/timesheets/controllers/timesheet.controller.ts index b8e6e7b..9f8d6fb 100644 --- a/src/time-and-attendance/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/timesheets/controllers/timesheet.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Req, UnauthorizedException } from "@nestjs/common"; +import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query, Req, UnauthorizedException } from "@nestjs/common"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/timesheets/services/timesheet-get-overview.service"; import { TimesheetApprovalService } from "src/time-and-attendance/timesheets/services/timesheet-approval.service"; @@ -17,11 +17,12 @@ export class TimesheetController { getTimesheetByPayPeriod( @Req() req, @Param('year', ParseIntPipe) year: number, - @Param('period_number', ParseIntPipe) period_number: number + @Param('period_number', ParseIntPipe) period_number: number, + @Query('employee_email') employee_email?: string, ) { const email = req.user?.email; if (!email) throw new UnauthorizedException('Unauthorized User'); - return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(email, year, period_number); + return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(email, year, period_number, employee_email); } @Patch('timesheet-approval') diff --git a/src/time-and-attendance/timesheets/dtos/timesheet.dto.ts b/src/time-and-attendance/timesheets/dtos/timesheet.dto.ts index a391684..9858077 100644 --- a/src/time-and-attendance/timesheets/dtos/timesheet.dto.ts +++ b/src/time-and-attendance/timesheets/dtos/timesheet.dto.ts @@ -17,16 +17,16 @@ export class Timesheet { @IsInt() timesheet_id: number; @IsBoolean() is_approved: boolean; @Type(() => TimesheetDay) days: TimesheetDay[]; - @Type(() => TotalHours) weekly_hours: TotalHours[]; - @Type(() => TotalExpenses) weekly_expenses: TotalExpenses[]; + @Type(() => TotalHours) weekly_hours: TotalHours; + @Type(() => TotalExpenses) weekly_expenses: TotalExpenses; } export class TimesheetDay { @IsString() date: string; @Type(() => Shift) shifts: Shift[]; @Type(() => Expense) expenses: Expense[]; - @Type(() => TotalHours) daily_hours: TotalHours[]; - @Type(() => TotalExpenses) daily_expenses: TotalExpenses[]; + @Type(() => TotalHours) daily_hours: TotalHours; + @Type(() => TotalExpenses) daily_expenses: TotalExpenses; } export class TotalHours { diff --git a/src/time-and-attendance/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/timesheets/services/timesheet-get-overview.service.ts index 2edddaa..6c8b008 100644 --- a/src/time-and-attendance/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/timesheets/services/timesheet-get-overview.service.ts @@ -35,14 +35,16 @@ export class GetTimesheetsOverviewService { //----------------------------------------------------------------------------------- // GET TIMESHEETS FOR A SELECTED EMPLOYEE //----------------------------------------------------------------------------------- - async getTimesheetsForEmployeeByPeriod(email: string, pay_year: number, pay_period_no: number): Promise> { + 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(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 @@ -132,8 +134,8 @@ export class GetTimesheetsOverviewService { expenses_by_date.set(date_string, arr); } //weekly totals - const weekly_hours: TotalHours[] = [emptyHours()]; - const weekly_expenses: TotalExpenses[] = [emptyExpenses()]; + const weekly_hours: TotalHours = emptyHours(); + const weekly_expenses: TotalExpenses = emptyExpenses(); //map of days const days = day_dates.map((date) => { @@ -169,15 +171,15 @@ export class GetTimesheetsOverviewService { })); //daily totals - const daily_hours = [emptyHours()]; - const daily_expenses = [emptyExpenses()]; + const daily_hours = emptyHours(); + const daily_expenses = emptyExpenses(); //totals by shift types for (const shift of shifts_source) { const hours = diffOfHours(shift.start_time, shift.end_time); const subgroup = hoursSubGroupFromBankCode(shift.bank_code); - daily_hours[0][subgroup] += hours; - weekly_hours[0][subgroup] += hours; + daily_hours[subgroup] += hours; + weekly_hours[subgroup] += hours; } //totals by expense types @@ -185,20 +187,20 @@ export class GetTimesheetsOverviewService { const subgroup = expenseSubgroupFromBankCode(expense.bank_code); if (subgroup === 'mileage') { const mileage = num(expense.mileage); - daily_expenses[0].mileage += mileage; - weekly_expenses[0].mileage += mileage; + daily_expenses.mileage += mileage; + weekly_expenses.mileage += mileage; } else if (subgroup === 'per_diem') { const amount = num(expense.amount); - daily_expenses[0].per_diem += amount; - weekly_expenses[0].per_diem += amount; + daily_expenses.per_diem += amount; + weekly_expenses.per_diem += amount; } else if (subgroup === 'on_call') { const amount = num(expense.amount); - daily_expenses[0].on_call += amount; - weekly_expenses[0].on_call += amount; + daily_expenses.on_call += amount; + weekly_expenses.on_call += amount; } else { const amount = num(expense.amount); - daily_expenses[0].expenses += amount; - weekly_expenses[0].expenses += amount; + daily_expenses.expenses += amount; + weekly_expenses.expenses += amount; } } return {