From 2ec59e2ffb281e108f615c54b4deaac0d631711f Mon Sep 17 00:00:00 2001 From: Nic D Date: Thu, 5 Feb 2026 08:00:54 -0500 Subject: [PATCH 1/5] feat(paid-time-off): add methods and controller to pull paid time off for one employee --- .../paid-time-off/paid-time-off.controller.ts | 22 ++++++++++++ .../paid-time-off/paid-time-off.module.ts | 8 +++-- .../paid-time-off/paid-time-off.service.ts | 34 +++++++++++++++++-- .../shifts/services/shifts-delete.service.ts | 4 +-- .../shifts/services/shifts-update.service.ts | 4 +-- .../shifts/shifts.module.ts | 4 +-- .../time-and-attendance.module.ts | 7 ++-- 7 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 src/time-and-attendance/paid-time-off/paid-time-off.controller.ts diff --git a/src/time-and-attendance/paid-time-off/paid-time-off.controller.ts b/src/time-and-attendance/paid-time-off/paid-time-off.controller.ts new file mode 100644 index 0000000..977ad77 --- /dev/null +++ b/src/time-and-attendance/paid-time-off/paid-time-off.controller.ts @@ -0,0 +1,22 @@ +import { Controller, Get, Query } from "@nestjs/common"; +import { Prisma } from "@prisma/client"; +import { Access } from "src/common/decorators/module-access.decorators"; +import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators"; +import { Result } from "src/common/errors/result-error.factory"; +import { PaidTimeOffBankHoursService } from "src/time-and-attendance/paid-time-off/paid-time-off.service"; + +@Controller('paid-time-off') +export class PaidTimeOffController { + constructor( + private readonly paidTimeOffService: PaidTimeOffBankHoursService, + ) { } + + @Get('totals') + @ModuleAccessAllowed('timesheets', 'timesheets_approval', 'employee_management') + async getPaidTimeOffTotalsForOneEmployee( + @Access('email') email: string, + @Query('email') employee_email?: string, + ) { + return this.paidTimeOffService.getPaidTimeOffTotalsWithEmployeeEmail(employee_email ?? email); + } +} \ No newline at end of file diff --git a/src/time-and-attendance/paid-time-off/paid-time-off.module.ts b/src/time-and-attendance/paid-time-off/paid-time-off.module.ts index f814c79..605e2a9 100644 --- a/src/time-and-attendance/paid-time-off/paid-time-off.module.ts +++ b/src/time-and-attendance/paid-time-off/paid-time-off.module.ts @@ -4,18 +4,20 @@ import { PrismaService } from "src/prisma/prisma.service"; import { BankedHoursService } from "src/time-and-attendance/domains/services/banking-hours.service"; import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; -import { PaidTimeOFfBankHoursService } from "src/time-and-attendance/paid-time-off/paid-time-off.service"; +import { PaidTimeOffController } from "src/time-and-attendance/paid-time-off/paid-time-off.controller"; +import { PaidTimeOffBankHoursService } from "src/time-and-attendance/paid-time-off/paid-time-off.service"; @Module({ + controllers: [PaidTimeOffController], providers: [ PrismaService, EmailToIdResolver, - PaidTimeOFfBankHoursService, + PaidTimeOffBankHoursService, VacationService, SickLeaveService, BankedHoursService, ], exports: [ - PaidTimeOFfBankHoursService, + PaidTimeOffBankHoursService, ], }) export class PaidTimeOffModule { } \ No newline at end of file diff --git a/src/time-and-attendance/paid-time-off/paid-time-off.service.ts b/src/time-and-attendance/paid-time-off/paid-time-off.service.ts index 2e05fd0..c8df7cc 100644 --- a/src/time-and-attendance/paid-time-off/paid-time-off.service.ts +++ b/src/time-and-attendance/paid-time-off/paid-time-off.service.ts @@ -1,21 +1,51 @@ import { Injectable } from "@nestjs/common"; import { Result } from "src/common/errors/result-error.factory"; +import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; import { computeHours } from "src/common/utils/date-utils"; import { PrismaService } from "src/prisma/prisma.service"; import { BankedHoursService } from "src/time-and-attendance/domains/services/banking-hours.service"; import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; -import { paid_time_off_mapping, paid_time_off_types } from "src/time-and-attendance/paid-time-off/paid-time-off.dto"; +import { paid_time_off_mapping, paid_time_off_types, PaidTimeOffDto } from "src/time-and-attendance/paid-time-off/paid-time-off.dto"; @Injectable() -export class PaidTimeOFfBankHoursService { +export class PaidTimeOffBankHoursService { constructor( private readonly prisma: PrismaService, private readonly bankingService: BankedHoursService, private readonly vacationService: VacationService, private readonly sickLeaveService: SickLeaveService, + private readonly emailResolver: EmailToIdResolver, ) { } + getPaidTimeOffTotalsWithEmployeeEmail = async (email: string): Promise, string>> => { + const employee_info = await this.emailResolver.findIdByEmail(email); + + + if (!employee_info.success) + return { success: false, error: 'USER_NOT_FOUND' } + + const pto = await this.prisma.paidTimeOff.findUnique({ + where: { employee_id: employee_info.data }, + select: { + vacation_hours: true, + sick_hours: true, + banked_hours: true, + } + }) + + if (!pto) + return { success: false, error: 'PTO_NOT_FOUND' } + + const ptoData: Partial = { + sick_hours: Number(pto.sick_hours), + vacation_hours: Number(pto.vacation_hours), + banked_hours: Number(pto.banked_hours), + } + + return { success: true, data: ptoData } + } + //called during update function of Shifts Module updatePaidTimeOffBankHoursWhenShiftUpdate = async ( start_time: Date, diff --git a/src/time-and-attendance/shifts/services/shifts-delete.service.ts b/src/time-and-attendance/shifts/services/shifts-delete.service.ts index cdf8878..2c291ce 100644 --- a/src/time-and-attendance/shifts/services/shifts-delete.service.ts +++ b/src/time-and-attendance/shifts/services/shifts-delete.service.ts @@ -3,14 +3,14 @@ import { Result } from "src/common/errors/result-error.factory"; import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; import { computeHours } from "src/common/utils/date-utils"; import { PrismaService } from "src/prisma/prisma.service"; -import { PaidTimeOFfBankHoursService } from "src/time-and-attendance/paid-time-off/paid-time-off.service"; +import { PaidTimeOffBankHoursService } from "src/time-and-attendance/paid-time-off/paid-time-off.service"; import { PayPeriodEventService } from "src/time-and-attendance/pay-period/services/pay-period-event.service"; @Injectable() export class ShiftsDeleteService { constructor( private readonly prisma: PrismaService, - private readonly paidTimeOffService: PaidTimeOFfBankHoursService, + private readonly paidTimeOffService: PaidTimeOffBankHoursService, private readonly emailResolver: EmailToIdResolver, private readonly payPeriodEventService: PayPeriodEventService, ) { } diff --git a/src/time-and-attendance/shifts/services/shifts-update.service.ts b/src/time-and-attendance/shifts/services/shifts-update.service.ts index dcaaf3d..8cef2be 100644 --- a/src/time-and-attendance/shifts/services/shifts-update.service.ts +++ b/src/time-and-attendance/shifts/services/shifts-update.service.ts @@ -10,7 +10,7 @@ import { shift_select } from "src/time-and-attendance/utils/selects.utils"; import { Normalized } from "src/time-and-attendance/utils/type.utils"; import { ShiftDto } from "src/time-and-attendance/shifts/shift.dto"; import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; -import { PaidTimeOFfBankHoursService } from "src/time-and-attendance/paid-time-off/paid-time-off.service"; +import { PaidTimeOffBankHoursService } from "src/time-and-attendance/paid-time-off/paid-time-off.service"; import { paid_time_off_types } from "src/time-and-attendance/paid-time-off/paid-time-off.dto"; import { PayPeriodEventService } from "src/time-and-attendance/pay-period/services/pay-period-event.service"; @@ -21,7 +21,7 @@ export class ShiftsUpdateService { private readonly typeResolver: BankCodesResolver, private readonly timesheetResolver: EmployeeTimesheetResolver, private readonly emailResolver: EmailToIdResolver, - private readonly paidTimeOffService: PaidTimeOFfBankHoursService, + private readonly paidTimeOffService: PaidTimeOffBankHoursService, private readonly payPeriodEventService: PayPeriodEventService, ) { } diff --git a/src/time-and-attendance/shifts/shifts.module.ts b/src/time-and-attendance/shifts/shifts.module.ts index 5d979e0..2374c38 100644 --- a/src/time-and-attendance/shifts/shifts.module.ts +++ b/src/time-and-attendance/shifts/shifts.module.ts @@ -8,7 +8,7 @@ import { ShiftsUpdateService } from 'src/time-and-attendance/shifts/services/shi import { VacationService } from 'src/time-and-attendance/domains/services/vacation.service'; import { BankedHoursService } from 'src/time-and-attendance/domains/services/banking-hours.service'; import { PaidTimeOffModule } from 'src/time-and-attendance/paid-time-off/paid-time-off.module'; -import { PaidTimeOFfBankHoursService } from 'src/time-and-attendance/paid-time-off/paid-time-off.service'; +import { PaidTimeOffBankHoursService } from 'src/time-and-attendance/paid-time-off/paid-time-off.service'; import { PayPeriodEventService } from 'src/time-and-attendance/pay-period/services/pay-period-event.service'; @Module({ @@ -20,7 +20,7 @@ import { PayPeriodEventService } from 'src/time-and-attendance/pay-period/servic ShiftsDeleteService, VacationService, BankedHoursService, - PaidTimeOFfBankHoursService, + PaidTimeOffBankHoursService, PayPeriodEventService, ], exports: [ diff --git a/src/time-and-attendance/time-and-attendance.module.ts b/src/time-and-attendance/time-and-attendance.module.ts index 7b7696c..1fb589d 100644 --- a/src/time-and-attendance/time-and-attendance.module.ts +++ b/src/time-and-attendance/time-and-attendance.module.ts @@ -3,7 +3,8 @@ import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-l import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; import { BankedHoursService } from "src/time-and-attendance/domains/services/banking-hours.service"; import { PaidTimeOffModule } from "src/time-and-attendance/paid-time-off/paid-time-off.module"; -import { PaidTimeOFfBankHoursService } from "src/time-and-attendance/paid-time-off/paid-time-off.service"; +import { PaidTimeOffController } from "src/time-and-attendance/paid-time-off/paid-time-off.controller"; +import { PaidTimeOffBankHoursService } from "src/time-and-attendance/paid-time-off/paid-time-off.service"; import { ExpenseController } from "src/time-and-attendance/expenses/expense.controller"; import { ExpenseCreateService } from "src/time-and-attendance/expenses/services/expense-create.service"; @@ -64,7 +65,7 @@ import { PayPeriodEventService } from "./pay-period/services/pay-period-event.se ExpenseController, PayPeriodsController, CsvExportController, - + PaidTimeOffController, ], providers: [ GetTimesheetsOverviewService, @@ -90,7 +91,7 @@ import { PayPeriodEventService } from "./pay-period/services/pay-period-event.se CsvGeneratorService, VacationService, BankedHoursService, - PaidTimeOFfBankHoursService, + PaidTimeOffBankHoursService, PayPeriodEventService, ], exports: [TimesheetApprovalService], From d0f68cf1c6692283768b3926e4968bb57f176cc0 Mon Sep 17 00:00:00 2001 From: Nic D Date: Thu, 5 Feb 2026 13:24:27 -0500 Subject: [PATCH 2/5] fix(auth): change logout behavior, now sends success code when logout request processed --- .../authentication/controllers/auth.controller.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/identity-and-account/authentication/controllers/auth.controller.ts b/src/identity-and-account/authentication/controllers/auth.controller.ts index 31e5f81..7abcba6 100644 --- a/src/identity-and-account/authentication/controllers/auth.controller.ts +++ b/src/identity-and-account/authentication/controllers/auth.controller.ts @@ -43,6 +43,8 @@ export class AuthController { response.clearCookie('connect.sid', { path: '/', }); + + response.sendStatus(200); }) } } From 6d96311d9840e9ff2a696affef2287cdb6f5bf7c Mon Sep 17 00:00:00 2001 From: Nic D Date: Thu, 5 Feb 2026 15:00:52 -0500 Subject: [PATCH 3/5] refactor(e2e): remove old e2e workflow, no longer up to date, will need to create new one later(never?) --- .github/workflows/e2e.yml | 33 ------------------- .../paid-time-off/REST.test.http | 1 + 2 files changed, 1 insertion(+), 33 deletions(-) delete mode 100644 .github/workflows/e2e.yml create mode 100644 src/time-and-attendance/paid-time-off/REST.test.http diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml deleted file mode 100644 index d1ccb54..0000000 --- a/.github/workflows/e2e.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: e2e -on: - push: - branches: [main] - pull_request: - -jobs: - e2e: - runs-on: ubuntu-latest - services: - postgres: - image: postgres:16 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: dev # ou test - ports: ["5432:5432"] - options: >- - --health-cmd="pg_isready -U postgres -d dev" - --health-interval=5s - --health-timeout=5s - --health-retries=20 - env: - DATABASE_URL: postgresql://postgres:postgres@localhost:5432/dev?schema=public - TZ: UTC - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: { node-version: '18' } - - run: npm ci - - run: npx prisma migrate deploy - - run: npm run seed:all - - run: npm run test:e2e:ci diff --git a/src/time-and-attendance/paid-time-off/REST.test.http b/src/time-and-attendance/paid-time-off/REST.test.http new file mode 100644 index 0000000..ecd228c --- /dev/null +++ b/src/time-and-attendance/paid-time-off/REST.test.http @@ -0,0 +1 @@ +GET http://localhost:3000/paid-time-off/totals \ No newline at end of file From a4be488a0f32d749965fd93344ad68a6b9bce924 Mon Sep 17 00:00:00 2001 From: Nic D Date: Fri, 6 Feb 2026 14:02:01 -0500 Subject: [PATCH 4/5] fix(sick-hours): fix a single error in shift creation where sick hours check was being done with banked hours --- .../domains/services/sick-leave.service.ts | 4 ++-- .../shifts/services/shifts-create.service.ts | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/time-and-attendance/domains/services/sick-leave.service.ts b/src/time-and-attendance/domains/services/sick-leave.service.ts index b7f17ea..9e5e226 100644 --- a/src/time-and-attendance/domains/services/sick-leave.service.ts +++ b/src/time-and-attendance/domains/services/sick-leave.service.ts @@ -122,7 +122,7 @@ export class SickLeaveService { id: true, paid_time_off: { select: { - banked_hours: true + sick_hours: true }, }, }, @@ -134,7 +134,7 @@ export class SickLeaveService { if (!employee.paid_time_off) { return { success: false, error: 'SICK_HOURS_BANK_NOT_FOUND' } as Result; } - const sick_bank = (employee.paid_time_off.banked_hours).toNumber(); + const sick_bank = (employee.paid_time_off.sick_hours).toNumber(); if (sick_bank <= 0) return { success: false, error: 'EMPTY_SICK_HOURS_BANK' } as Result; if (asked_hours > sick_bank) { diff --git a/src/time-and-attendance/shifts/services/shifts-create.service.ts b/src/time-and-attendance/shifts/services/shifts-create.service.ts index 9ae63d7..f415dc8 100644 --- a/src/time-and-attendance/shifts/services/shifts-create.service.ts +++ b/src/time-and-attendance/shifts/services/shifts-create.service.ts @@ -144,7 +144,8 @@ export class ShiftsCreateService { } if (!result.success) return { success: false, error: result.error }; - const valid_hours = result.data / 1.5; + + const valid_hours = result.data; adjusted_end_time = new Date(normed_shift.data.start_time); adjusted_end_time.setHours(adjusted_end_time.getHours() + valid_hours); } From afa8694e1ed9b6c7df0e98ad04257174de9d5fde Mon Sep 17 00:00:00 2001 From: Nic D Date: Fri, 6 Feb 2026 14:37:27 -0500 Subject: [PATCH 5/5] fix(sick-hours): fix issue where sicks hours are not properly being deducted nor reimbursed --- .../domains/services/sick-leave.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/time-and-attendance/domains/services/sick-leave.service.ts b/src/time-and-attendance/domains/services/sick-leave.service.ts index 9e5e226..f13a230 100644 --- a/src/time-and-attendance/domains/services/sick-leave.service.ts +++ b/src/time-and-attendance/domains/services/sick-leave.service.ts @@ -143,11 +143,11 @@ export class SickLeaveService { await tx.paidTimeOff.update({ where: { employee_id: employee.id }, data: { - banked_hours: { decrement: asked_hours }, + sick_hours: { decrement: asked_hours }, last_updated: new Date(), }, - select: { banked_hours: true }, }); + return { success: true, data: asked_hours } as Result; } });