From 3e2265cbacc1abcd774577640e9413443b323cfb Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Thu, 23 Oct 2025 14:23:53 -0400 Subject: [PATCH 01/57] BREAKING CHANGE(Time&Attendance): prep to split into APIs. setup structure for timesheet related modules --- .../domains}/business-logics.module.ts | 0 .../domains}/services/after-hours.service.ts | 0 .../domains}/services/holiday.service.ts | 0 .../domains}/services/mileage.service.ts | 0 .../domains}/services/overtime.service.ts | 0 .../domains}/services/sick-leave.service.ts | 0 .../domains}/services/vacation.service.ts | 0 .../controllers/leave-requests.controller.ts | 0 .../dtos/leave-request-view.dto.ts | 0 .../dtos/upsert-leave-request.dto.ts | 0 .../leave-requests/leave-requests.module.ts | 0 .../mappers/leave-requests-archive.mapper.ts | 0 .../mappers/leave-requests.mapper.ts | 0 .../holiday-leave-requests.service.ts | 0 .../services/leave-request.service.ts | 0 .../services/sick-leave-requests.service.ts | 0 .../vacation-leave-requests.service.ts | 0 .../utils/leave-request.transform.ts | 0 .../utils/leave-request.util.ts | 0 .../utils/leave-requests-archive.select.ts | 0 .../utils/leave-requests.select.ts | 0 .../controllers/pay-periods.controller.ts | 0 .../dtos/bulk-crew-approval.dto.ts | 0 .../pay-period}/dtos/bundle-pay-period.dto.ts | 0 .../dtos/overview-employee-period.dto.ts | 0 .../dtos/overview-pay-period.dto.ts | 0 .../pay-period}/dtos/pay-period.dto.ts | 0 .../pay-period}/mappers/pay-periods.mapper.ts | 0 .../modules/pay-period}/pay-periods.module.ts | 4 ++-- .../services/pay-periods-command.service.ts | 0 .../services/pay-periods-query.service.ts | 0 .../pay-period}/utils/pay-year.util.ts | 0 .../shared/constants/date-time.constant.ts | 0 .../shared/constants/regex.constant.ts | 0 .../shared/constants/utils.constant.ts | 0 .../shared/helpers/date-time.helpers.ts | 0 .../shared/interfaces/shifts.interface.ts | 0 .../modules/shared/selects/expenses.select.ts | 0 .../shared/selects/pay-periods.select.ts | 0 .../modules/shared/selects/shifts.select.ts | 0 .../modules/shared/shared.module.ts | 0 .../shared/types/upsert-actions.types.ts | 0 .../utils/resolve-bank-type-id.utils.ts | 0 .../shared/utils/resolve-email-id.utils.ts | 0 .../shared/utils/resolve-full-name.utils.ts | 0 .../shared/utils/resolve-shifts-id.utils.ts | 0 .../shared/utils/resolve-timesheet.utils.ts | 2 +- .../controller/schedule-presets.controller.ts | 2 +- .../dtos/create-schedule-preset-shifts.dto.ts | 0 .../dtos/create-schedule-presets.dto.ts | 0 .../mappers/schedule-presets.mappers.ts | 0 .../schedule-presets.module.ts | 0 .../schedule-presets-apply.service.ts | 2 +- .../schedule-presets-command.service.ts | 6 ++--- .../schedule-presets-query.service.ts | 2 +- .../types/schedule-presets.types.ts | 0 .../shifts/controllers/shift.controller.ts | 0 .../shifts/dtos/get-shift.dto.ts | 0 .../time-tracker}/shifts/dtos/shift.dto.ts | 0 .../shifts/dtos/update-shift.dto.ts | 0 .../helpers/shifts-date-time-helpers.ts | 0 .../services/shifts-archival.service.ts | 0 .../shifts/services/shifts-get.service.ts | 0 .../shifts/services/shifts-upsert.service.ts | 0 .../time-tracker}/shifts/shifts.module.ts | 4 ++-- .../get-shift-overview.dto.ts | 0 .../shifts-command.service.ts | 0 .../shifts-overview-row.interface.ts | 0 .../shifts-query.service.ts | 0 .../shifts-upsert.types.ts | 0 .../shifts.controller.ts | 0 .../~misc_deprecated-files/shifts.helpers.ts | 0 .../~misc_deprecated-files/shifts.utils.ts | 0 .../upsert-shift.dto.ts | 0 .../controllers/timesheet.controller.ts | 2 +- .../timesheets/dtos/timesheet.dto.ts | 0 .../helpers/timesheets-date-time-helpers.ts | 0 .../services/timesheet-approval.service.ts | 0 .../services/timesheet-archive.service.ts | 0 .../timesheet-get-overview.service.ts | 0 .../timesheets/timesheets.module.ts | 23 +++++++++++++++++++ .../create-timesheet.dto.ts | 0 .../search-timesheet.dto.ts | 0 .../timesheet-period.dto.ts | 0 .../timesheet.helpers.ts | 0 .../timesheet.mappers.ts | 0 .../timesheet.selectors.ts | 0 .../~misc_deprecated-files/timesheet.types.ts | 0 .../~misc_deprecated-files/timesheet.utils.ts | 0 .../timesheets-command.service.ts | 0 .../timesheets-query.service.ts | 0 .../timesheets.controller.ts | 0 src/app.module.ts | 6 ++--- .../controllers/shifts-archive.controller.ts | 2 +- .../timesheets-archive.controller.ts | 2 +- .../archival/services/archival.service.ts | 4 ++-- src/modules/employees/employees.module.ts | 2 +- src/modules/exports/csv-exports.module.ts | 2 +- src/modules/preferences/preferences.module.ts | 2 +- .../services/preferences.service.ts | 2 +- src/modules/timesheets/timesheets.module.ts | 10 ++++---- 101 files changed, 51 insertions(+), 28 deletions(-) rename src/{modules/business-logics => Time_And_Attendance/domains}/business-logics.module.ts (100%) rename src/{modules/business-logics => Time_And_Attendance/domains}/services/after-hours.service.ts (100%) rename src/{modules/business-logics => Time_And_Attendance/domains}/services/holiday.service.ts (100%) rename src/{modules/business-logics => Time_And_Attendance/domains}/services/mileage.service.ts (100%) rename src/{modules/business-logics => Time_And_Attendance/domains}/services/overtime.service.ts (100%) rename src/{modules/business-logics => Time_And_Attendance/domains}/services/sick-leave.service.ts (100%) rename src/{modules/business-logics => Time_And_Attendance/domains}/services/vacation.service.ts (100%) rename src/{ => Time_And_Attendance}/modules/leave-requests/controllers/leave-requests.controller.ts (100%) rename src/{ => Time_And_Attendance}/modules/leave-requests/dtos/leave-request-view.dto.ts (100%) rename src/{ => Time_And_Attendance}/modules/leave-requests/dtos/upsert-leave-request.dto.ts (100%) rename src/{ => Time_And_Attendance}/modules/leave-requests/leave-requests.module.ts (100%) rename src/{ => Time_And_Attendance}/modules/leave-requests/mappers/leave-requests-archive.mapper.ts (100%) rename src/{ => Time_And_Attendance}/modules/leave-requests/mappers/leave-requests.mapper.ts (100%) rename src/{ => Time_And_Attendance}/modules/leave-requests/services/holiday-leave-requests.service.ts (100%) rename src/{ => Time_And_Attendance}/modules/leave-requests/services/leave-request.service.ts (100%) rename src/{ => Time_And_Attendance}/modules/leave-requests/services/sick-leave-requests.service.ts (100%) rename src/{ => Time_And_Attendance}/modules/leave-requests/services/vacation-leave-requests.service.ts (100%) rename src/{ => Time_And_Attendance}/modules/leave-requests/utils/leave-request.transform.ts (100%) rename src/{ => Time_And_Attendance}/modules/leave-requests/utils/leave-request.util.ts (100%) rename src/{ => Time_And_Attendance}/modules/leave-requests/utils/leave-requests-archive.select.ts (100%) rename src/{ => Time_And_Attendance}/modules/leave-requests/utils/leave-requests.select.ts (100%) rename src/{modules/pay-periods => Time_And_Attendance/modules/pay-period}/controllers/pay-periods.controller.ts (100%) rename src/{modules/pay-periods => Time_And_Attendance/modules/pay-period}/dtos/bulk-crew-approval.dto.ts (100%) rename src/{modules/pay-periods => Time_And_Attendance/modules/pay-period}/dtos/bundle-pay-period.dto.ts (100%) rename src/{modules/pay-periods => Time_And_Attendance/modules/pay-period}/dtos/overview-employee-period.dto.ts (100%) rename src/{modules/pay-periods => Time_And_Attendance/modules/pay-period}/dtos/overview-pay-period.dto.ts (100%) rename src/{modules/pay-periods => Time_And_Attendance/modules/pay-period}/dtos/pay-period.dto.ts (100%) rename src/{modules/pay-periods => Time_And_Attendance/modules/pay-period}/mappers/pay-periods.mapper.ts (100%) rename src/{modules/pay-periods => Time_And_Attendance/modules/pay-period}/pay-periods.module.ts (78%) rename src/{modules/pay-periods => Time_And_Attendance/modules/pay-period}/services/pay-periods-command.service.ts (100%) rename src/{modules/pay-periods => Time_And_Attendance/modules/pay-period}/services/pay-periods-query.service.ts (100%) rename src/{modules/pay-periods => Time_And_Attendance/modules/pay-period}/utils/pay-year.util.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/constants/date-time.constant.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/constants/regex.constant.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/constants/utils.constant.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/helpers/date-time.helpers.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/interfaces/shifts.interface.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/selects/expenses.select.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/selects/pay-periods.select.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/selects/shifts.select.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/shared.module.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/types/upsert-actions.types.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/utils/resolve-bank-type-id.utils.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/utils/resolve-email-id.utils.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/utils/resolve-full-name.utils.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/utils/resolve-shifts-id.utils.ts (100%) rename src/{ => Time_And_Attendance}/modules/shared/utils/resolve-timesheet.utils.ts (89%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/schedule-presets/controller/schedule-presets.controller.ts (94%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/schedule-presets/dtos/create-schedule-presets.dto.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/schedule-presets/mappers/schedule-presets.mappers.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/schedule-presets/schedule-presets.module.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/schedule-presets/services/schedule-presets-apply.service.ts (98%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/schedule-presets/services/schedule-presets-command.service.ts (96%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/schedule-presets/services/schedule-presets-query.service.ts (95%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/schedule-presets/types/schedule-presets.types.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/controllers/shift.controller.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/dtos/get-shift.dto.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/dtos/shift.dto.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/dtos/update-shift.dto.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/helpers/shifts-date-time-helpers.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/services/shifts-archival.service.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/services/shifts-get.service.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/services/shifts-upsert.service.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/shifts.module.ts (82%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/~misc_deprecated-files/get-shift-overview.dto.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/~misc_deprecated-files/shifts-command.service.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/~misc_deprecated-files/shifts-overview-row.interface.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/~misc_deprecated-files/shifts-query.service.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/~misc_deprecated-files/shifts-upsert.types.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/~misc_deprecated-files/shifts.controller.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/~misc_deprecated-files/shifts.helpers.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/~misc_deprecated-files/shifts.utils.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/shifts/~misc_deprecated-files/upsert-shift.dto.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/controllers/timesheet.controller.ts (91%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/dtos/timesheet.dto.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/helpers/timesheets-date-time-helpers.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/services/timesheet-approval.service.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/services/timesheet-archive.service.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/services/timesheet-get-overview.service.ts (100%) create mode 100644 src/Time_And_Attendance/modules/time-tracker/timesheets/timesheets.module.ts rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/~misc_deprecated-files/create-timesheet.dto.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/~misc_deprecated-files/search-timesheet.dto.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/~misc_deprecated-files/timesheet-period.dto.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/~misc_deprecated-files/timesheet.helpers.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/~misc_deprecated-files/timesheet.mappers.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/~misc_deprecated-files/timesheet.selectors.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/~misc_deprecated-files/timesheet.types.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/~misc_deprecated-files/timesheet.utils.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/~misc_deprecated-files/timesheets-command.service.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/~misc_deprecated-files/timesheets-query.service.ts (100%) rename src/{modules => Time_And_Attendance/modules/time-tracker}/timesheets/~misc_deprecated-files/timesheets.controller.ts (100%) diff --git a/src/modules/business-logics/business-logics.module.ts b/src/Time_And_Attendance/domains/business-logics.module.ts similarity index 100% rename from src/modules/business-logics/business-logics.module.ts rename to src/Time_And_Attendance/domains/business-logics.module.ts diff --git a/src/modules/business-logics/services/after-hours.service.ts b/src/Time_And_Attendance/domains/services/after-hours.service.ts similarity index 100% rename from src/modules/business-logics/services/after-hours.service.ts rename to src/Time_And_Attendance/domains/services/after-hours.service.ts diff --git a/src/modules/business-logics/services/holiday.service.ts b/src/Time_And_Attendance/domains/services/holiday.service.ts similarity index 100% rename from src/modules/business-logics/services/holiday.service.ts rename to src/Time_And_Attendance/domains/services/holiday.service.ts diff --git a/src/modules/business-logics/services/mileage.service.ts b/src/Time_And_Attendance/domains/services/mileage.service.ts similarity index 100% rename from src/modules/business-logics/services/mileage.service.ts rename to src/Time_And_Attendance/domains/services/mileage.service.ts diff --git a/src/modules/business-logics/services/overtime.service.ts b/src/Time_And_Attendance/domains/services/overtime.service.ts similarity index 100% rename from src/modules/business-logics/services/overtime.service.ts rename to src/Time_And_Attendance/domains/services/overtime.service.ts diff --git a/src/modules/business-logics/services/sick-leave.service.ts b/src/Time_And_Attendance/domains/services/sick-leave.service.ts similarity index 100% rename from src/modules/business-logics/services/sick-leave.service.ts rename to src/Time_And_Attendance/domains/services/sick-leave.service.ts diff --git a/src/modules/business-logics/services/vacation.service.ts b/src/Time_And_Attendance/domains/services/vacation.service.ts similarity index 100% rename from src/modules/business-logics/services/vacation.service.ts rename to src/Time_And_Attendance/domains/services/vacation.service.ts diff --git a/src/modules/leave-requests/controllers/leave-requests.controller.ts b/src/Time_And_Attendance/modules/leave-requests/controllers/leave-requests.controller.ts similarity index 100% rename from src/modules/leave-requests/controllers/leave-requests.controller.ts rename to src/Time_And_Attendance/modules/leave-requests/controllers/leave-requests.controller.ts diff --git a/src/modules/leave-requests/dtos/leave-request-view.dto.ts b/src/Time_And_Attendance/modules/leave-requests/dtos/leave-request-view.dto.ts similarity index 100% rename from src/modules/leave-requests/dtos/leave-request-view.dto.ts rename to src/Time_And_Attendance/modules/leave-requests/dtos/leave-request-view.dto.ts diff --git a/src/modules/leave-requests/dtos/upsert-leave-request.dto.ts b/src/Time_And_Attendance/modules/leave-requests/dtos/upsert-leave-request.dto.ts similarity index 100% rename from src/modules/leave-requests/dtos/upsert-leave-request.dto.ts rename to src/Time_And_Attendance/modules/leave-requests/dtos/upsert-leave-request.dto.ts diff --git a/src/modules/leave-requests/leave-requests.module.ts b/src/Time_And_Attendance/modules/leave-requests/leave-requests.module.ts similarity index 100% rename from src/modules/leave-requests/leave-requests.module.ts rename to src/Time_And_Attendance/modules/leave-requests/leave-requests.module.ts diff --git a/src/modules/leave-requests/mappers/leave-requests-archive.mapper.ts b/src/Time_And_Attendance/modules/leave-requests/mappers/leave-requests-archive.mapper.ts similarity index 100% rename from src/modules/leave-requests/mappers/leave-requests-archive.mapper.ts rename to src/Time_And_Attendance/modules/leave-requests/mappers/leave-requests-archive.mapper.ts diff --git a/src/modules/leave-requests/mappers/leave-requests.mapper.ts b/src/Time_And_Attendance/modules/leave-requests/mappers/leave-requests.mapper.ts similarity index 100% rename from src/modules/leave-requests/mappers/leave-requests.mapper.ts rename to src/Time_And_Attendance/modules/leave-requests/mappers/leave-requests.mapper.ts diff --git a/src/modules/leave-requests/services/holiday-leave-requests.service.ts b/src/Time_And_Attendance/modules/leave-requests/services/holiday-leave-requests.service.ts similarity index 100% rename from src/modules/leave-requests/services/holiday-leave-requests.service.ts rename to src/Time_And_Attendance/modules/leave-requests/services/holiday-leave-requests.service.ts diff --git a/src/modules/leave-requests/services/leave-request.service.ts b/src/Time_And_Attendance/modules/leave-requests/services/leave-request.service.ts similarity index 100% rename from src/modules/leave-requests/services/leave-request.service.ts rename to src/Time_And_Attendance/modules/leave-requests/services/leave-request.service.ts diff --git a/src/modules/leave-requests/services/sick-leave-requests.service.ts b/src/Time_And_Attendance/modules/leave-requests/services/sick-leave-requests.service.ts similarity index 100% rename from src/modules/leave-requests/services/sick-leave-requests.service.ts rename to src/Time_And_Attendance/modules/leave-requests/services/sick-leave-requests.service.ts diff --git a/src/modules/leave-requests/services/vacation-leave-requests.service.ts b/src/Time_And_Attendance/modules/leave-requests/services/vacation-leave-requests.service.ts similarity index 100% rename from src/modules/leave-requests/services/vacation-leave-requests.service.ts rename to src/Time_And_Attendance/modules/leave-requests/services/vacation-leave-requests.service.ts diff --git a/src/modules/leave-requests/utils/leave-request.transform.ts b/src/Time_And_Attendance/modules/leave-requests/utils/leave-request.transform.ts similarity index 100% rename from src/modules/leave-requests/utils/leave-request.transform.ts rename to src/Time_And_Attendance/modules/leave-requests/utils/leave-request.transform.ts diff --git a/src/modules/leave-requests/utils/leave-request.util.ts b/src/Time_And_Attendance/modules/leave-requests/utils/leave-request.util.ts similarity index 100% rename from src/modules/leave-requests/utils/leave-request.util.ts rename to src/Time_And_Attendance/modules/leave-requests/utils/leave-request.util.ts diff --git a/src/modules/leave-requests/utils/leave-requests-archive.select.ts b/src/Time_And_Attendance/modules/leave-requests/utils/leave-requests-archive.select.ts similarity index 100% rename from src/modules/leave-requests/utils/leave-requests-archive.select.ts rename to src/Time_And_Attendance/modules/leave-requests/utils/leave-requests-archive.select.ts diff --git a/src/modules/leave-requests/utils/leave-requests.select.ts b/src/Time_And_Attendance/modules/leave-requests/utils/leave-requests.select.ts similarity index 100% rename from src/modules/leave-requests/utils/leave-requests.select.ts rename to src/Time_And_Attendance/modules/leave-requests/utils/leave-requests.select.ts diff --git a/src/modules/pay-periods/controllers/pay-periods.controller.ts b/src/Time_And_Attendance/modules/pay-period/controllers/pay-periods.controller.ts similarity index 100% rename from src/modules/pay-periods/controllers/pay-periods.controller.ts rename to src/Time_And_Attendance/modules/pay-period/controllers/pay-periods.controller.ts diff --git a/src/modules/pay-periods/dtos/bulk-crew-approval.dto.ts b/src/Time_And_Attendance/modules/pay-period/dtos/bulk-crew-approval.dto.ts similarity index 100% rename from src/modules/pay-periods/dtos/bulk-crew-approval.dto.ts rename to src/Time_And_Attendance/modules/pay-period/dtos/bulk-crew-approval.dto.ts diff --git a/src/modules/pay-periods/dtos/bundle-pay-period.dto.ts b/src/Time_And_Attendance/modules/pay-period/dtos/bundle-pay-period.dto.ts similarity index 100% rename from src/modules/pay-periods/dtos/bundle-pay-period.dto.ts rename to src/Time_And_Attendance/modules/pay-period/dtos/bundle-pay-period.dto.ts diff --git a/src/modules/pay-periods/dtos/overview-employee-period.dto.ts b/src/Time_And_Attendance/modules/pay-period/dtos/overview-employee-period.dto.ts similarity index 100% rename from src/modules/pay-periods/dtos/overview-employee-period.dto.ts rename to src/Time_And_Attendance/modules/pay-period/dtos/overview-employee-period.dto.ts diff --git a/src/modules/pay-periods/dtos/overview-pay-period.dto.ts b/src/Time_And_Attendance/modules/pay-period/dtos/overview-pay-period.dto.ts similarity index 100% rename from src/modules/pay-periods/dtos/overview-pay-period.dto.ts rename to src/Time_And_Attendance/modules/pay-period/dtos/overview-pay-period.dto.ts diff --git a/src/modules/pay-periods/dtos/pay-period.dto.ts b/src/Time_And_Attendance/modules/pay-period/dtos/pay-period.dto.ts similarity index 100% rename from src/modules/pay-periods/dtos/pay-period.dto.ts rename to src/Time_And_Attendance/modules/pay-period/dtos/pay-period.dto.ts diff --git a/src/modules/pay-periods/mappers/pay-periods.mapper.ts b/src/Time_And_Attendance/modules/pay-period/mappers/pay-periods.mapper.ts similarity index 100% rename from src/modules/pay-periods/mappers/pay-periods.mapper.ts rename to src/Time_And_Attendance/modules/pay-period/mappers/pay-periods.mapper.ts diff --git a/src/modules/pay-periods/pay-periods.module.ts b/src/Time_And_Attendance/modules/pay-period/pay-periods.module.ts similarity index 78% rename from src/modules/pay-periods/pay-periods.module.ts rename to src/Time_And_Attendance/modules/pay-period/pay-periods.module.ts index c614179..fabd257 100644 --- a/src/modules/pay-periods/pay-periods.module.ts +++ b/src/Time_And_Attendance/modules/pay-period/pay-periods.module.ts @@ -2,10 +2,10 @@ import { PrismaModule } from "src/prisma/prisma.module"; import { PayPeriodsController } from "./controllers/pay-periods.controller"; import { Module } from "@nestjs/common"; import { PayPeriodsQueryService } from "./services/pay-periods-query.service"; -import { TimesheetsModule } from "../timesheets/timesheets.module"; +import { TimesheetsModule } from "../time-tracker/timesheets/timesheets.module"; import { SharedModule } from "../shared/shared.module"; import { PrismaService } from "src/prisma/prisma.service"; -import { BusinessLogicsModule } from "../business-logics/business-logics.module"; +import { BusinessLogicsModule } from "../../../modules/business-logics/business-logics.module"; @Module({ imports: [PrismaModule, TimesheetsModule, SharedModule, BusinessLogicsModule], diff --git a/src/modules/pay-periods/services/pay-periods-command.service.ts b/src/Time_And_Attendance/modules/pay-period/services/pay-periods-command.service.ts similarity index 100% rename from src/modules/pay-periods/services/pay-periods-command.service.ts rename to src/Time_And_Attendance/modules/pay-period/services/pay-periods-command.service.ts diff --git a/src/modules/pay-periods/services/pay-periods-query.service.ts b/src/Time_And_Attendance/modules/pay-period/services/pay-periods-query.service.ts similarity index 100% rename from src/modules/pay-periods/services/pay-periods-query.service.ts rename to src/Time_And_Attendance/modules/pay-period/services/pay-periods-query.service.ts diff --git a/src/modules/pay-periods/utils/pay-year.util.ts b/src/Time_And_Attendance/modules/pay-period/utils/pay-year.util.ts similarity index 100% rename from src/modules/pay-periods/utils/pay-year.util.ts rename to src/Time_And_Attendance/modules/pay-period/utils/pay-year.util.ts diff --git a/src/modules/shared/constants/date-time.constant.ts b/src/Time_And_Attendance/modules/shared/constants/date-time.constant.ts similarity index 100% rename from src/modules/shared/constants/date-time.constant.ts rename to src/Time_And_Attendance/modules/shared/constants/date-time.constant.ts diff --git a/src/modules/shared/constants/regex.constant.ts b/src/Time_And_Attendance/modules/shared/constants/regex.constant.ts similarity index 100% rename from src/modules/shared/constants/regex.constant.ts rename to src/Time_And_Attendance/modules/shared/constants/regex.constant.ts diff --git a/src/modules/shared/constants/utils.constant.ts b/src/Time_And_Attendance/modules/shared/constants/utils.constant.ts similarity index 100% rename from src/modules/shared/constants/utils.constant.ts rename to src/Time_And_Attendance/modules/shared/constants/utils.constant.ts diff --git a/src/modules/shared/helpers/date-time.helpers.ts b/src/Time_And_Attendance/modules/shared/helpers/date-time.helpers.ts similarity index 100% rename from src/modules/shared/helpers/date-time.helpers.ts rename to src/Time_And_Attendance/modules/shared/helpers/date-time.helpers.ts diff --git a/src/modules/shared/interfaces/shifts.interface.ts b/src/Time_And_Attendance/modules/shared/interfaces/shifts.interface.ts similarity index 100% rename from src/modules/shared/interfaces/shifts.interface.ts rename to src/Time_And_Attendance/modules/shared/interfaces/shifts.interface.ts diff --git a/src/modules/shared/selects/expenses.select.ts b/src/Time_And_Attendance/modules/shared/selects/expenses.select.ts similarity index 100% rename from src/modules/shared/selects/expenses.select.ts rename to src/Time_And_Attendance/modules/shared/selects/expenses.select.ts diff --git a/src/modules/shared/selects/pay-periods.select.ts b/src/Time_And_Attendance/modules/shared/selects/pay-periods.select.ts similarity index 100% rename from src/modules/shared/selects/pay-periods.select.ts rename to src/Time_And_Attendance/modules/shared/selects/pay-periods.select.ts diff --git a/src/modules/shared/selects/shifts.select.ts b/src/Time_And_Attendance/modules/shared/selects/shifts.select.ts similarity index 100% rename from src/modules/shared/selects/shifts.select.ts rename to src/Time_And_Attendance/modules/shared/selects/shifts.select.ts diff --git a/src/modules/shared/shared.module.ts b/src/Time_And_Attendance/modules/shared/shared.module.ts similarity index 100% rename from src/modules/shared/shared.module.ts rename to src/Time_And_Attendance/modules/shared/shared.module.ts diff --git a/src/modules/shared/types/upsert-actions.types.ts b/src/Time_And_Attendance/modules/shared/types/upsert-actions.types.ts similarity index 100% rename from src/modules/shared/types/upsert-actions.types.ts rename to src/Time_And_Attendance/modules/shared/types/upsert-actions.types.ts diff --git a/src/modules/shared/utils/resolve-bank-type-id.utils.ts b/src/Time_And_Attendance/modules/shared/utils/resolve-bank-type-id.utils.ts similarity index 100% rename from src/modules/shared/utils/resolve-bank-type-id.utils.ts rename to src/Time_And_Attendance/modules/shared/utils/resolve-bank-type-id.utils.ts diff --git a/src/modules/shared/utils/resolve-email-id.utils.ts b/src/Time_And_Attendance/modules/shared/utils/resolve-email-id.utils.ts similarity index 100% rename from src/modules/shared/utils/resolve-email-id.utils.ts rename to src/Time_And_Attendance/modules/shared/utils/resolve-email-id.utils.ts diff --git a/src/modules/shared/utils/resolve-full-name.utils.ts b/src/Time_And_Attendance/modules/shared/utils/resolve-full-name.utils.ts similarity index 100% rename from src/modules/shared/utils/resolve-full-name.utils.ts rename to src/Time_And_Attendance/modules/shared/utils/resolve-full-name.utils.ts diff --git a/src/modules/shared/utils/resolve-shifts-id.utils.ts b/src/Time_And_Attendance/modules/shared/utils/resolve-shifts-id.utils.ts similarity index 100% rename from src/modules/shared/utils/resolve-shifts-id.utils.ts rename to src/Time_And_Attendance/modules/shared/utils/resolve-shifts-id.utils.ts diff --git a/src/modules/shared/utils/resolve-timesheet.utils.ts b/src/Time_And_Attendance/modules/shared/utils/resolve-timesheet.utils.ts similarity index 89% rename from src/modules/shared/utils/resolve-timesheet.utils.ts rename to src/Time_And_Attendance/modules/shared/utils/resolve-timesheet.utils.ts index 61f4ce6..2c5e515 100644 --- a/src/modules/shared/utils/resolve-timesheet.utils.ts +++ b/src/Time_And_Attendance/modules/shared/utils/resolve-timesheet.utils.ts @@ -1,6 +1,6 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { Prisma, PrismaClient } from "@prisma/client"; -import { weekStartSunday } from "src/modules/shifts/helpers/shifts-date-time-helpers"; +import { weekStartSunday } from "src/Time_And_Attendance/modules/time-tracker/shifts/helpers/shifts-date-time-helpers"; import { PrismaService } from "src/prisma/prisma.service"; import { EmailToIdResolver } from "./resolve-email-id.utils"; diff --git a/src/modules/schedule-presets/controller/schedule-presets.controller.ts b/src/Time_And_Attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts similarity index 94% rename from src/modules/schedule-presets/controller/schedule-presets.controller.ts rename to src/Time_And_Attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index b031c67..0efb934 100644 --- a/src/modules/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/Time_And_Attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -1,7 +1,7 @@ import { BadRequestException, Body, Controller, Get, NotFoundException, Param, Post, Put, Query } from "@nestjs/common"; import { SchedulePresetsDto } from "../dtos/create-schedule-presets.dto"; import { SchedulePresetsCommandService } from "../services/schedule-presets-command.service"; -import { UpsertAction } from "src/modules/shared/types/upsert-actions.types"; +import { UpsertAction } from "src/Time_And_Attendance/modules/shared/types/upsert-actions.types"; import { SchedulePresetsQueryService } from "../services/schedule-presets-query.service"; @Controller('schedule-presets') diff --git a/src/modules/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts b/src/Time_And_Attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts similarity index 100% rename from src/modules/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts rename to src/Time_And_Attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts diff --git a/src/modules/schedule-presets/dtos/create-schedule-presets.dto.ts b/src/Time_And_Attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts similarity index 100% rename from src/modules/schedule-presets/dtos/create-schedule-presets.dto.ts rename to src/Time_And_Attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts diff --git a/src/modules/schedule-presets/mappers/schedule-presets.mappers.ts b/src/Time_And_Attendance/modules/time-tracker/schedule-presets/mappers/schedule-presets.mappers.ts similarity index 100% rename from src/modules/schedule-presets/mappers/schedule-presets.mappers.ts rename to src/Time_And_Attendance/modules/time-tracker/schedule-presets/mappers/schedule-presets.mappers.ts diff --git a/src/modules/schedule-presets/schedule-presets.module.ts b/src/Time_And_Attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts similarity index 100% rename from src/modules/schedule-presets/schedule-presets.module.ts rename to src/Time_And_Attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts diff --git a/src/modules/schedule-presets/services/schedule-presets-apply.service.ts b/src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts similarity index 98% rename from src/modules/schedule-presets/services/schedule-presets-apply.service.ts rename to src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts index fa2bce1..e2e8dbf 100644 --- a/src/modules/schedule-presets/services/schedule-presets-apply.service.ts +++ b/src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts @@ -1,5 +1,5 @@ import { BadRequestException, ConflictException, Injectable, NotFoundException } from "@nestjs/common"; -import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/Time_And_Attendance/modules/shared/utils/resolve-email-id.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { ApplyResult } from "../types/schedule-presets.types"; import { Prisma, Weekday } from "@prisma/client"; diff --git a/src/modules/schedule-presets/services/schedule-presets-command.service.ts b/src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts similarity index 96% rename from src/modules/schedule-presets/services/schedule-presets-command.service.ts rename to src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts index 0c2a8bb..5c1cb2c 100644 --- a/src/modules/schedule-presets/services/schedule-presets-command.service.ts +++ b/src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts @@ -1,7 +1,7 @@ import { BadRequestException, ConflictException, Injectable, NotFoundException } from "@nestjs/common"; -import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils"; -import { UpsertAction } from "src/modules/shared/types/upsert-actions.types"; +import { BankCodesResolver } from "src/Time_And_Attendance/modules/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/Time_And_Attendance/modules/shared/utils/resolve-email-id.utils"; +import { UpsertAction } from "src/Time_And_Attendance/modules/shared/types/upsert-actions.types"; import { PrismaService } from "src/prisma/prisma.service"; import { SchedulePresetsDto } from "../dtos/create-schedule-presets.dto"; import { Prisma, Weekday } from "@prisma/client"; diff --git a/src/modules/schedule-presets/services/schedule-presets-query.service.ts b/src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts similarity index 95% rename from src/modules/schedule-presets/services/schedule-presets-query.service.ts rename to src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts index 7ccb0f0..7b87cef 100644 --- a/src/modules/schedule-presets/services/schedule-presets-query.service.ts +++ b/src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts @@ -1,5 +1,5 @@ import { Injectable, NotFoundException } from "@nestjs/common"; -import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/Time_And_Attendance/modules/shared/utils/resolve-email-id.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { PresetResponse, ShiftResponse } from "../types/schedule-presets.types"; import { Prisma } from "@prisma/client"; diff --git a/src/modules/schedule-presets/types/schedule-presets.types.ts b/src/Time_And_Attendance/modules/time-tracker/schedule-presets/types/schedule-presets.types.ts similarity index 100% rename from src/modules/schedule-presets/types/schedule-presets.types.ts rename to src/Time_And_Attendance/modules/time-tracker/schedule-presets/types/schedule-presets.types.ts diff --git a/src/modules/shifts/controllers/shift.controller.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/controllers/shift.controller.ts similarity index 100% rename from src/modules/shifts/controllers/shift.controller.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/controllers/shift.controller.ts diff --git a/src/modules/shifts/dtos/get-shift.dto.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/dtos/get-shift.dto.ts similarity index 100% rename from src/modules/shifts/dtos/get-shift.dto.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/dtos/get-shift.dto.ts diff --git a/src/modules/shifts/dtos/shift.dto.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/dtos/shift.dto.ts similarity index 100% rename from src/modules/shifts/dtos/shift.dto.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/dtos/shift.dto.ts diff --git a/src/modules/shifts/dtos/update-shift.dto.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/dtos/update-shift.dto.ts similarity index 100% rename from src/modules/shifts/dtos/update-shift.dto.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/dtos/update-shift.dto.ts diff --git a/src/modules/shifts/helpers/shifts-date-time-helpers.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/helpers/shifts-date-time-helpers.ts similarity index 100% rename from src/modules/shifts/helpers/shifts-date-time-helpers.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/helpers/shifts-date-time-helpers.ts diff --git a/src/modules/shifts/services/shifts-archival.service.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts similarity index 100% rename from src/modules/shifts/services/shifts-archival.service.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts diff --git a/src/modules/shifts/services/shifts-get.service.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-get.service.ts similarity index 100% rename from src/modules/shifts/services/shifts-get.service.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-get.service.ts diff --git a/src/modules/shifts/services/shifts-upsert.service.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts similarity index 100% rename from src/modules/shifts/services/shifts-upsert.service.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts diff --git a/src/modules/shifts/shifts.module.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/shifts.module.ts similarity index 82% rename from src/modules/shifts/shifts.module.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/shifts.module.ts index 4c20e0d..5d33c35 100644 --- a/src/modules/shifts/shifts.module.ts +++ b/src/Time_And_Attendance/modules/time-tracker/shifts/shifts.module.ts @@ -1,10 +1,10 @@ import { ShiftsArchivalService } from './services/shifts-archival.service'; import { BusinessLogicsModule } from 'src/modules/business-logics/business-logics.module'; -import { NotificationsModule } from '../notifications/notifications.module'; +import { NotificationsModule } from '../../../../modules/notifications/notifications.module'; import { ShiftsUpsertService } from './services/shifts-upsert.service'; import { ShiftsGetService } from './services/shifts-get.service'; import { ShiftController } from './controllers/shift.controller'; -import { SharedModule } from '../shared/shared.module'; +import { SharedModule } from '../../shared/shared.module'; import { Module } from '@nestjs/common'; @Module({ diff --git a/src/modules/shifts/~misc_deprecated-files/get-shift-overview.dto.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/get-shift-overview.dto.ts similarity index 100% rename from src/modules/shifts/~misc_deprecated-files/get-shift-overview.dto.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/get-shift-overview.dto.ts diff --git a/src/modules/shifts/~misc_deprecated-files/shifts-command.service.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-command.service.ts similarity index 100% rename from src/modules/shifts/~misc_deprecated-files/shifts-command.service.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-command.service.ts diff --git a/src/modules/shifts/~misc_deprecated-files/shifts-overview-row.interface.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-overview-row.interface.ts similarity index 100% rename from src/modules/shifts/~misc_deprecated-files/shifts-overview-row.interface.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-overview-row.interface.ts diff --git a/src/modules/shifts/~misc_deprecated-files/shifts-query.service.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-query.service.ts similarity index 100% rename from src/modules/shifts/~misc_deprecated-files/shifts-query.service.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-query.service.ts diff --git a/src/modules/shifts/~misc_deprecated-files/shifts-upsert.types.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-upsert.types.ts similarity index 100% rename from src/modules/shifts/~misc_deprecated-files/shifts-upsert.types.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-upsert.types.ts diff --git a/src/modules/shifts/~misc_deprecated-files/shifts.controller.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.controller.ts similarity index 100% rename from src/modules/shifts/~misc_deprecated-files/shifts.controller.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.controller.ts diff --git a/src/modules/shifts/~misc_deprecated-files/shifts.helpers.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.helpers.ts similarity index 100% rename from src/modules/shifts/~misc_deprecated-files/shifts.helpers.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.helpers.ts diff --git a/src/modules/shifts/~misc_deprecated-files/shifts.utils.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.utils.ts similarity index 100% rename from src/modules/shifts/~misc_deprecated-files/shifts.utils.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.utils.ts diff --git a/src/modules/shifts/~misc_deprecated-files/upsert-shift.dto.ts b/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/upsert-shift.dto.ts similarity index 100% rename from src/modules/shifts/~misc_deprecated-files/upsert-shift.dto.ts rename to src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/upsert-shift.dto.ts diff --git a/src/modules/timesheets/controllers/timesheet.controller.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts similarity index 91% rename from src/modules/timesheets/controllers/timesheet.controller.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts index 1efb2fe..61eef3a 100644 --- a/src/modules/timesheets/controllers/timesheet.controller.ts +++ b/src/Time_And_Attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts @@ -1,4 +1,4 @@ -import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/Time_And_Attendance/modules/shared/utils/resolve-email-id.utils"; import { GetTimesheetsOverviewService } from "../services/timesheet-get-overview.service"; import { BadRequestException, Controller, Get, Query} from "@nestjs/common"; diff --git a/src/modules/timesheets/dtos/timesheet.dto.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/dtos/timesheet.dto.ts similarity index 100% rename from src/modules/timesheets/dtos/timesheet.dto.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/dtos/timesheet.dto.ts diff --git a/src/modules/timesheets/helpers/timesheets-date-time-helpers.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/helpers/timesheets-date-time-helpers.ts similarity index 100% rename from src/modules/timesheets/helpers/timesheets-date-time-helpers.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/helpers/timesheets-date-time-helpers.ts diff --git a/src/modules/timesheets/services/timesheet-approval.service.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-approval.service.ts similarity index 100% rename from src/modules/timesheets/services/timesheet-approval.service.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-approval.service.ts diff --git a/src/modules/timesheets/services/timesheet-archive.service.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-archive.service.ts similarity index 100% rename from src/modules/timesheets/services/timesheet-archive.service.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-archive.service.ts diff --git a/src/modules/timesheets/services/timesheet-get-overview.service.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts similarity index 100% rename from src/modules/timesheets/services/timesheet-get-overview.service.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/timesheets.module.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/timesheets.module.ts new file mode 100644 index 0000000..001fb19 --- /dev/null +++ b/src/Time_And_Attendance/modules/time-tracker/timesheets/timesheets.module.ts @@ -0,0 +1,23 @@ +import { GetTimesheetsOverviewService } from './services/timesheet-get-overview.service'; +import { TimesheetArchiveService } from './services/timesheet-archive.service'; +import { BusinessLogicsModule } from 'src/modules/business-logics/business-logics.module'; +import { TimesheetController } from './controllers/timesheet.controller'; +import { SharedModule } from '../../shared/shared.module'; +import { ShiftsModule } from '../shifts/shifts.module'; +import { Module } from '@nestjs/common'; + +@Module({ + imports: [ + BusinessLogicsModule, + SharedModule, + ShiftsModule, + ], + controllers: [TimesheetController], + providers: [ + TimesheetArchiveService, + GetTimesheetsOverviewService, + SharedModule, + ], + exports: [], +}) +export class TimesheetsModule {} diff --git a/src/modules/timesheets/~misc_deprecated-files/create-timesheet.dto.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/create-timesheet.dto.ts similarity index 100% rename from src/modules/timesheets/~misc_deprecated-files/create-timesheet.dto.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/create-timesheet.dto.ts diff --git a/src/modules/timesheets/~misc_deprecated-files/search-timesheet.dto.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/search-timesheet.dto.ts similarity index 100% rename from src/modules/timesheets/~misc_deprecated-files/search-timesheet.dto.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/search-timesheet.dto.ts diff --git a/src/modules/timesheets/~misc_deprecated-files/timesheet-period.dto.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet-period.dto.ts similarity index 100% rename from src/modules/timesheets/~misc_deprecated-files/timesheet-period.dto.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet-period.dto.ts diff --git a/src/modules/timesheets/~misc_deprecated-files/timesheet.helpers.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.helpers.ts similarity index 100% rename from src/modules/timesheets/~misc_deprecated-files/timesheet.helpers.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.helpers.ts diff --git a/src/modules/timesheets/~misc_deprecated-files/timesheet.mappers.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.mappers.ts similarity index 100% rename from src/modules/timesheets/~misc_deprecated-files/timesheet.mappers.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.mappers.ts diff --git a/src/modules/timesheets/~misc_deprecated-files/timesheet.selectors.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.selectors.ts similarity index 100% rename from src/modules/timesheets/~misc_deprecated-files/timesheet.selectors.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.selectors.ts diff --git a/src/modules/timesheets/~misc_deprecated-files/timesheet.types.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.types.ts similarity index 100% rename from src/modules/timesheets/~misc_deprecated-files/timesheet.types.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.types.ts diff --git a/src/modules/timesheets/~misc_deprecated-files/timesheet.utils.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.utils.ts similarity index 100% rename from src/modules/timesheets/~misc_deprecated-files/timesheet.utils.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.utils.ts diff --git a/src/modules/timesheets/~misc_deprecated-files/timesheets-command.service.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-command.service.ts similarity index 100% rename from src/modules/timesheets/~misc_deprecated-files/timesheets-command.service.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-command.service.ts diff --git a/src/modules/timesheets/~misc_deprecated-files/timesheets-query.service.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-query.service.ts similarity index 100% rename from src/modules/timesheets/~misc_deprecated-files/timesheets-query.service.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-query.service.ts diff --git a/src/modules/timesheets/~misc_deprecated-files/timesheets.controller.ts b/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets.controller.ts similarity index 100% rename from src/modules/timesheets/~misc_deprecated-files/timesheets.controller.ts rename to src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets.controller.ts diff --git a/src/app.module.ts b/src/app.module.ts index 4179229..60c2767 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -18,15 +18,15 @@ import { OvertimeService } from './modules/business-logics/services/overtime.se import { PreferencesModule } from './modules/preferences/preferences.module'; import { PrismaModule } from './prisma/prisma.module'; import { ScheduleModule } from '@nestjs/schedule'; -import { ShiftsModule } from './modules/shifts/shifts.module'; -import { TimesheetsModule } from './modules/timesheets/timesheets.module'; +import { ShiftsModule } from './Time_And_Attendance/modules/time-tracker/shifts/shifts.module'; +import { TimesheetsModule } from './Time_And_Attendance/modules/time-tracker/timesheets/timesheets.module'; import { UsersModule } from './modules/users-management/users.module'; import { ConfigModule } from '@nestjs/config'; import { APP_FILTER, APP_PIPE } from '@nestjs/core'; import { HttpExceptionFilter } from './common/filters/http-exception.filter'; import { ValidationError } from 'class-validator'; import { SchedulePresetsModule } from './modules/schedule-presets/schedule-presets.module'; -import { PayperiodsModule } from './modules/pay-periods/pay-periods.module'; +import { PayperiodsModule } from './Time_And_Attendance/modules/pay-period/pay-periods.module'; @Module({ imports: [ diff --git a/src/modules/archival/controllers/shifts-archive.controller.ts b/src/modules/archival/controllers/shifts-archive.controller.ts index e8f92f2..463ea4a 100644 --- a/src/modules/archival/controllers/shifts-archive.controller.ts +++ b/src/modules/archival/controllers/shifts-archive.controller.ts @@ -2,7 +2,7 @@ import { Get, Param, ParseIntPipe, NotFoundException, Controller, UseGuards } fr import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { ShiftsArchive, Roles as RoleEnum } from "@prisma/client"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { ShiftsArchivalService } from "src/modules/shifts/services/shifts-archival.service"; +import { ShiftsArchivalService } from "src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-archival.service"; @ApiTags('Shift Archives') // @UseGuards() diff --git a/src/modules/archival/controllers/timesheets-archive.controller.ts b/src/modules/archival/controllers/timesheets-archive.controller.ts index 7505b66..9be0116 100644 --- a/src/modules/archival/controllers/timesheets-archive.controller.ts +++ b/src/modules/archival/controllers/timesheets-archive.controller.ts @@ -2,7 +2,7 @@ import { Controller, Get, NotFoundException, Param, ParseIntPipe, UseGuards } fr import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { TimesheetsArchive, Roles as RoleEnum } from '@prisma/client'; -import { TimesheetArchiveService } from "src/modules/timesheets/services/timesheet-archive.service"; +import { TimesheetArchiveService } from "src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-archive.service"; @ApiTags('Timesheet Archives') // @UseGuards() diff --git a/src/modules/archival/services/archival.service.ts b/src/modules/archival/services/archival.service.ts index 66be2d0..898ab51 100644 --- a/src/modules/archival/services/archival.service.ts +++ b/src/modules/archival/services/archival.service.ts @@ -1,8 +1,8 @@ import { Injectable, Logger } from "@nestjs/common"; import { Cron } from "@nestjs/schedule"; import { ExpensesArchivalService } from "src/modules/expenses/services/expenses-archival.service"; -import { ShiftsArchivalService } from "src/modules/shifts/services/shifts-archival.service"; -import { TimesheetArchiveService } from "src/modules/timesheets/services/timesheet-archive.service"; +import { ShiftsArchivalService } from "src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-archival.service"; +import { TimesheetArchiveService } from "src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-archive.service"; @Injectable() export class ArchivalService { diff --git a/src/modules/employees/employees.module.ts b/src/modules/employees/employees.module.ts index 0f0be93..2fd40ee 100644 --- a/src/modules/employees/employees.module.ts +++ b/src/modules/employees/employees.module.ts @@ -2,7 +2,7 @@ import { Module } from '@nestjs/common'; import { EmployeesController } from './controllers/employees.controller'; import { EmployeesService } from './services/employees.service'; import { EmployeesArchivalService } from './services/employees-archival.service'; -import { SharedModule } from '../shared/shared.module'; +import { SharedModule } from '../../Time_And_Attendance/modules/shared/shared.module'; @Module({ imports: [SharedModule], diff --git a/src/modules/exports/csv-exports.module.ts b/src/modules/exports/csv-exports.module.ts index e034c9e..0c9f00b 100644 --- a/src/modules/exports/csv-exports.module.ts +++ b/src/modules/exports/csv-exports.module.ts @@ -1,7 +1,7 @@ import { Module } from "@nestjs/common"; import { CsvExportController } from "./controllers/csv-exports.controller"; import { CsvExportService } from "./services/csv-exports.service"; -import { SharedModule } from "../shared/shared.module"; +import { SharedModule } from "../../Time_And_Attendance/modules/shared/shared.module"; @Module({ providers:[CsvExportService, SharedModule], diff --git a/src/modules/preferences/preferences.module.ts b/src/modules/preferences/preferences.module.ts index 4fe0227..59031ba 100644 --- a/src/modules/preferences/preferences.module.ts +++ b/src/modules/preferences/preferences.module.ts @@ -1,7 +1,7 @@ import { Module } from "@nestjs/common"; import { PreferencesController } from "./controllers/preferences.controller"; import { PreferencesService } from "./services/preferences.service"; -import { SharedModule } from "../shared/shared.module"; +import { SharedModule } from "../../Time_And_Attendance/modules/shared/shared.module"; @Module({ imports: [SharedModule], diff --git a/src/modules/preferences/services/preferences.service.ts b/src/modules/preferences/services/preferences.service.ts index 6e4f169..2677573 100644 --- a/src/modules/preferences/services/preferences.service.ts +++ b/src/modules/preferences/services/preferences.service.ts @@ -2,7 +2,7 @@ import { Injectable } from "@nestjs/common"; import { Preferences } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; import { PreferencesDto } from "../dtos/preferences.dto"; -import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/Time_And_Attendance/modules/shared/utils/resolve-email-id.utils"; @Injectable() export class PreferencesService { diff --git a/src/modules/timesheets/timesheets.module.ts b/src/modules/timesheets/timesheets.module.ts index be44043..e1c997e 100644 --- a/src/modules/timesheets/timesheets.module.ts +++ b/src/modules/timesheets/timesheets.module.ts @@ -1,9 +1,9 @@ -import { GetTimesheetsOverviewService } from './services/timesheet-get-overview.service'; -import { TimesheetArchiveService } from './services/timesheet-archive.service'; +import { GetTimesheetsOverviewService } from '../../Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service'; +import { TimesheetArchiveService } from '../../Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-archive.service'; import { BusinessLogicsModule } from 'src/modules/business-logics/business-logics.module'; -import { TimesheetController } from './controllers/timesheet.controller'; -import { SharedModule } from '../shared/shared.module'; -import { ShiftsModule } from '../shifts/shifts.module'; +import { TimesheetController } from '../../Time_And_Attendance/modules/time-tracker/timesheets/controllers/timesheet.controller'; +import { SharedModule } from '../../Time_And_Attendance/modules/shared/shared.module'; +import { ShiftsModule } from '../../Time_And_Attendance/modules/time-tracker/shifts/shifts.module'; import { Module } from '@nestjs/common'; @Module({ From d16c4d4d86f9ddffe0061a02066e33dca4aaeaa3 Mon Sep 17 00:00:00 2001 From: Nicolas Drolet Date: Fri, 24 Oct 2025 14:23:26 -0400 Subject: [PATCH 02/57] fix(shift): minor fix for receiving date properly. Change 'd' for 'day' in date-time-helpers. 'Also remove oauth-sessions, unused bloat --- src/app.module.ts | 2 - .../controllers/oauth-sessions.controller.ts | 61 ------------- .../dtos/create-oauth-session.dto.ts | 70 --------------- .../dtos/update-oauth-session.dto.ts | 4 - .../oauth-sessions/oauth-sessions.module.ts | 10 --- .../services/oauth-sessions.service.ts | 86 ------------------- .../helpers/timesheets-date-time-helpers.ts | 2 +- 7 files changed, 1 insertion(+), 234 deletions(-) delete mode 100644 src/modules/oauth-sessions/controllers/oauth-sessions.controller.ts delete mode 100644 src/modules/oauth-sessions/dtos/create-oauth-session.dto.ts delete mode 100644 src/modules/oauth-sessions/dtos/update-oauth-session.dto.ts delete mode 100644 src/modules/oauth-sessions/oauth-sessions.module.ts delete mode 100644 src/modules/oauth-sessions/services/oauth-sessions.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 4179229..a748775 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -13,7 +13,6 @@ import { HealthModule } from './health/health.module'; import { HealthController } from './health/health.controller'; // import { LeaveRequestsModule } from './modules/leave-requests/leave-requests.module'; import { NotificationsModule } from './modules/notifications/notifications.module'; -import { OauthSessionsModule } from './modules/oauth-sessions/oauth-sessions.module'; import { OvertimeService } from './modules/business-logics/services/overtime.service'; import { PreferencesModule } from './modules/preferences/preferences.module'; import { PrismaModule } from './prisma/prisma.module'; @@ -42,7 +41,6 @@ import { PayperiodsModule } from './modules/pay-periods/pay-periods.module'; HealthModule, // LeaveRequestsModule, NotificationsModule, - OauthSessionsModule, PayperiodsModule, PreferencesModule, PrismaModule, diff --git a/src/modules/oauth-sessions/controllers/oauth-sessions.controller.ts b/src/modules/oauth-sessions/controllers/oauth-sessions.controller.ts deleted file mode 100644 index 2e5b54c..0000000 --- a/src/modules/oauth-sessions/controllers/oauth-sessions.controller.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Body, Controller, Delete, Get, Param, Patch, Post, UseGuards } from '@nestjs/common'; -import { OAuthSessions } from '@prisma/client'; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { Roles as RoleEnum } from '.prisma/client'; -import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; -import { CreateOauthSessionDto } from '../dtos/create-oauth-session.dto'; -import { OauthSessionsService } from '../services/oauth-sessions.service'; -import { UpdateOauthSessionDto } from '../dtos/update-oauth-session.dto'; - -@ApiTags('OAuth Sessions') -@ApiBearerAuth('sessions') -//@UseGuards(JwtAuthGuard) -@Controller('oauth-sessions') -export class OauthSessionsController { - constructor(private readonly oauthSessionsService: OauthSessionsService){} - - @Post() - // @RolesAllowed(RoleEnum.ADMIN) - @ApiOperation({summary: 'Create OAuth session' }) - @ApiResponse({ status: 201, description: 'OAuth session created', type: CreateOauthSessionDto }) - @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' }) - create(@Body()dto: CreateOauthSessionDto): Promise { - return this.oauthSessionsService.create(dto); - } - - @Get() - //@RolesAllowed(RoleEnum.ADMIN) - @ApiOperation({summary: 'Find all OAuth session' }) - @ApiResponse({ status: 201, description: 'List of OAuth session found', type: CreateOauthSessionDto, isArray: true }) - @ApiResponse({ status: 400, description: 'List of OAuth session not found' }) - findAll(): Promise { - return this.oauthSessionsService.findAll(); - } - - @Get(':id') - //@RolesAllowed(RoleEnum.ADMIN) - @ApiOperation({summary: 'Find OAuth session' }) - @ApiResponse({ status: 201, description: 'OAuth session found', type: CreateOauthSessionDto }) - @ApiResponse({ status: 400, description: 'OAuth session not found' }) - findOne(@Param('id') id: string): Promise { - return this.oauthSessionsService.findOne(id); - } - - @Patch(':id') - //@RolesAllowed(RoleEnum.ADMIN) - @ApiOperation({summary: 'Update OAuth session' }) - @ApiResponse({ status: 201, description: 'OAuth session updated', type: CreateOauthSessionDto }) - @ApiResponse({ status: 400, description: 'OAuth session not found' }) - update(@Param('id') id: string, @Body() dto: UpdateOauthSessionDto): Promise { - return this.oauthSessionsService.update(id,dto); - } - - @Delete(':id') - //@RolesAllowed(RoleEnum.ADMIN) - @ApiOperation({summary: 'Delete OAuth session' }) - @ApiResponse({ status: 201, description: 'OAuth session deleted', type: CreateOauthSessionDto }) - @ApiResponse({ status: 400, description: 'OAuth session not found' }) - remove(@Param('id') id: string): Promise { - return this.oauthSessionsService.remove(id); - } -} diff --git a/src/modules/oauth-sessions/dtos/create-oauth-session.dto.ts b/src/modules/oauth-sessions/dtos/create-oauth-session.dto.ts deleted file mode 100644 index ef36d90..0000000 --- a/src/modules/oauth-sessions/dtos/create-oauth-session.dto.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { ApiProperty } from "@nestjs/swagger"; -import { Type } from "class-transformer"; -import { IsArray, IsDate, IsOptional, IsString, IsUUID } from "class-validator"; - -export class CreateOauthSessionDto { - @ApiProperty({ - example: 'cklwi0vb70000z2z20q6f19qk', - description: 'Unique ID of an OAuth token (auto-generated)', - }) - id: string; - - @ApiProperty({ - example: 'S7A2U8R7O6N6', - description: 'User`s unique identification number', - }) - @IsUUID() - user_id: string; - - @ApiProperty({ - example: 'app.targo.ca', - description: 'URL in which the access token is used for', - }) - @IsString() - application: string; - - @IsString() - sid: string; - - @ApiProperty({ - example: 'L5O6R4D3/O6F3#T8H4E3&R6I4N6G4S7 ...', - description: 'Access token', - }) - @IsString() - access_token: string; - - @ApiProperty({ - example: 'Th3731102h1p07Th3R1n92', - description: 'Refresh token', - }) - @IsString() - refresh_token: string; - - @ApiProperty({ - example: '25/12/3018', - description: 'Access token`s expiry date', - }) - @Type(()=> Date) - @IsDate() - access_token_expiry: Date; - - @ApiProperty({ - example: '26/02/3019', - description: 'Refresh token`s expiry date', - required: false, - }) - @Type(()=> Date) - @IsDate() - @IsOptional() - refresh_token_expiry?: Date; - - @ApiProperty({ - example: 'access tolkiens, email, etc... ', - description: 'scopes of infos linked to the access token', - required: false, - }) - @IsArray() - @IsString() - @IsOptional() - scopes?: string[]; -} diff --git a/src/modules/oauth-sessions/dtos/update-oauth-session.dto.ts b/src/modules/oauth-sessions/dtos/update-oauth-session.dto.ts deleted file mode 100644 index 697efd7..0000000 --- a/src/modules/oauth-sessions/dtos/update-oauth-session.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from "@nestjs/swagger"; -import { CreateOauthSessionDto } from "./create-oauth-session.dto"; - -export class UpdateOauthSessionDto extends PartialType(CreateOauthSessionDto) {} diff --git a/src/modules/oauth-sessions/oauth-sessions.module.ts b/src/modules/oauth-sessions/oauth-sessions.module.ts deleted file mode 100644 index e68a481..0000000 --- a/src/modules/oauth-sessions/oauth-sessions.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { OauthSessionsController } from './controllers/oauth-sessions.controller'; -import { OauthSessionsService } from './services/oauth-sessions.service'; - -@Module({ - controllers: [OauthSessionsController], - providers: [OauthSessionsService, PrismaService] -}) -export class OauthSessionsModule {} diff --git a/src/modules/oauth-sessions/services/oauth-sessions.service.ts b/src/modules/oauth-sessions/services/oauth-sessions.service.ts deleted file mode 100644 index c307682..0000000 --- a/src/modules/oauth-sessions/services/oauth-sessions.service.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { CreateOauthSessionDto } from '../dtos/create-oauth-session.dto'; -import { OAuthSessions } from '@prisma/client'; -import { UpdateOauthSessionDto } from '../dtos/update-oauth-session.dto'; - -@Injectable() -export class OauthSessionsService { - constructor(private readonly prisma: PrismaService) {} - - async create(dto: CreateOauthSessionDto): Promise { - const { - user_id, - application, - access_token, - refresh_token, - sid, - access_token_expiry, - refresh_token_expiry, - scopes, - } = dto; - - return this.prisma.oAuthSessions.create({ - data: { - user_id, - application, - access_token, - refresh_token, - sid, - access_token_expiry, - refresh_token_expiry, - scopes, - }, - include: { user: true }, - }); - } - - findAll(): Promise { - return this.prisma.oAuthSessions.findMany({ - include: { user: true }, - }); - } - - async findOne(id: string): Promise { - const token = await this.prisma.oAuthSessions.findUnique({ - where: { id }, - include: { user: true }, - }); - if(!token) { - throw new NotFoundException(`token #${ id } not found`); - } - return token; - } - - async update(id: string, dto: UpdateOauthSessionDto): Promise { - await this.findOne(id); - const { - user_id, - application, - access_token, - refresh_token, - access_token_expiry, - refresh_token_expiry, - scopes, - } = dto; - - return this.prisma.oAuthSessions.update({ - where: { id }, - data: { - ...(user_id !== undefined && { user_id }), - ...(application !== undefined && { application }), - ...(access_token !== undefined && { access_token }), - ...(refresh_token !== undefined && { refresh_token }), - ...(access_token_expiry !== undefined && { access_token_expiry }), - ...(refresh_token_expiry !== undefined && { refresh_token_expiry }), - ...(scopes !== undefined && { scopes }), - }, - include: { user: true }, - }); - } - - async remove(id: string): Promise { - await this.findOne(id); - return this.prisma.oAuthSessions.delete({ where: { id }}); - } -} diff --git a/src/modules/timesheets/helpers/timesheets-date-time-helpers.ts b/src/modules/timesheets/helpers/timesheets-date-time-helpers.ts index 4a77edf..e0b9c78 100644 --- a/src/modules/timesheets/helpers/timesheets-date-time-helpers.ts +++ b/src/modules/timesheets/helpers/timesheets-date-time-helpers.ts @@ -15,7 +15,7 @@ export const toStringFromDate = (date: Date | string): string => { const year = d.getUTCFullYear(); const month = String(d.getUTCMonth() + 1).padStart(2, '0'); const day = String(d.getUTCDate()).padStart(2, '0'); - return `${year}-${month}-${d}`; + return `${year}-${month}-${day}`; } export const toHHmmFromDate = (input: Date | string): string => { From 062b9b4640e1c07e54498109f66847d5bb0c2116 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 24 Oct 2025 16:15:54 -0400 Subject: [PATCH 03/57] refactor(expenses): major refactor of the CRUDs methods sing sessions data --- package-lock.json | 136 +++++++------- package.json | 4 +- prisma/schema.prisma | 62 +------ .../controllers/expense.controller.ts | 27 ++- src/modules/expenses/dtos/expense.dto.ts | 4 +- src/modules/expenses/dtos/get-expense.dto.ts | 3 +- src/modules/expenses/expenses.module.ts | 34 ++-- .../helpers/expenses-date-time-helpers.ts | 4 +- .../services/expense-upsert.service.ts | 174 ++++++++++++++++-- .../shifts/controllers/shift.controller.ts | 12 +- .../shifts/services/shifts-upsert.service.ts | 16 +- 11 files changed, 277 insertions(+), 199 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1180132..b3363fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@nestjs/platform-express": "^11.1.6", "@nestjs/schedule": "^6.0.0", "@nestjs/swagger": "^11.2.0", - "@prisma/client": "^6.17.1", + "@prisma/client": "^6.18.0", "bullmq": "^5.58.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.2", @@ -54,7 +54,7 @@ "globals": "^16.0.0", "jest": "^29.7.0", "prettier": "^3.4.2", - "prisma": "^6.17.1", + "prisma": "^6.18.0", "source-map-support": "^0.5.21", "supertest": "^7.0.0", "ts-jest": "^29.2.5", @@ -3268,13 +3268,13 @@ } }, "node_modules/@nestjs/common": { - "version": "11.1.6", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.6.tgz", - "integrity": "sha512-krKwLLcFmeuKDqngG2N/RuZHCs2ycsKcxWIDgcm7i1lf3sQ0iG03ci+DsP/r3FcT/eJDFsIHnKtNta2LIi7PzQ==", + "version": "11.1.7", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.7.tgz", + "integrity": "sha512-lwlObwGgIlpXSXYOTpfzdCepUyWomz6bv9qzGzzvpgspUxkj0Uz0fUJcvD44V8Ps7QhKW3lZBoYbXrH25UZrbA==", "dependencies": { "file-type": "21.0.0", "iterare": "1.2.1", - "load-esm": "1.0.2", + "load-esm": "1.0.3", "tslib": "2.8.1", "uid": "2.0.2" }, @@ -3312,15 +3312,15 @@ } }, "node_modules/@nestjs/core": { - "version": "11.1.6", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.6.tgz", - "integrity": "sha512-siWX7UDgErisW18VTeJA+x+/tpNZrJewjTBsRPF3JVxuWRuAB1kRoiJcxHgln8Lb5UY9NdvklITR84DUEXD0Cg==", + "version": "11.1.7", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.7.tgz", + "integrity": "sha512-TyXFOwjhHv/goSgJ8i20K78jwTM0iSpk9GBcC2h3mf4MxNy+znI8m7nWjfoACjTkb89cTwDQetfTHtSfGLLaiA==", "hasInstallScript": true, "dependencies": { "@nuxt/opencollective": "0.4.1", "fast-safe-stringify": "2.1.1", "iterare": "1.2.1", - "path-to-regexp": "8.2.0", + "path-to-regexp": "8.3.0", "tslib": "2.8.1", "uid": "2.0.2" }, @@ -3392,14 +3392,14 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "11.1.6", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.6.tgz", - "integrity": "sha512-HErwPmKnk+loTq8qzu1up+k7FC6Kqa8x6lJ4cDw77KnTxLzsCaPt+jBvOq6UfICmfqcqCCf3dKXg+aObQp+kIQ==", + "version": "11.1.7", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.7.tgz", + "integrity": "sha512-5T+GLdvTiGPKB4/P4PM9ftKUKNHJy8ThEFhZA3vQnXVL7Vf0rDr07TfVTySVu+XTh85m1lpFVuyFM6u6wLNsRA==", "dependencies": { "cors": "2.8.5", "express": "5.1.0", "multer": "2.0.2", - "path-to-regexp": "8.2.0", + "path-to-regexp": "8.3.0", "tslib": "2.8.1" }, "funding": { @@ -3547,19 +3547,10 @@ } } }, - "node_modules/@nestjs/swagger/node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/@nestjs/testing": { - "version": "11.1.6", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.6.tgz", - "integrity": "sha512-srYzzDNxGvVCe1j0SpTS9/ix75PKt6Sn6iMaH1rpJ6nj2g8vwNrhK0CoJJXvpCYgrnI+2WES2pprYnq8rAMYHA==", + "version": "11.1.7", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.7.tgz", + "integrity": "sha512-QbtrgSlc3QVo6RHNxTTlyhaiobLLy8kvhOlgWHsoXRknybuRs7vZg4k5mo3ye6pITGeT3CrWIRpZjUsh5Wps5Q==", "dev": true, "dependencies": { "tslib": "2.8.1" @@ -3667,9 +3658,9 @@ } }, "node_modules/@prisma/client": { - "version": "6.17.1", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.17.1.tgz", - "integrity": "sha512-zL58jbLzYamjnNnmNA51IOZdbk5ci03KviXCuB0Tydc9btH2kDWsi1pQm2VecviRTM7jGia0OPPkgpGnT3nKvw==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.18.0.tgz", + "integrity": "sha512-jnL2I9gDnPnw4A+4h5SuNn8Gc+1mL1Z79U/3I9eE2gbxJG1oSA+62ByPW4xkeDgwE0fqMzzpAZ7IHxYnLZ4iQA==", "hasInstallScript": true, "engines": { "node": ">=18.18" @@ -3688,60 +3679,60 @@ } }, "node_modules/@prisma/config": { - "version": "6.17.1", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.17.1.tgz", - "integrity": "sha512-fs8wY6DsvOCzuiyWVckrVs1LOcbY4LZNz8ki4uUIQ28jCCzojTGqdLhN2Jl5lDnC1yI8/gNIKpsWDM8pLhOdwA==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.18.0.tgz", + "integrity": "sha512-rgFzspCpwsE+q3OF/xkp0fI2SJ3PfNe9LLMmuSVbAZ4nN66WfBiKqJKo/hLz3ysxiPQZf8h1SMf2ilqPMeWATQ==", "devOptional": true, "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", - "effect": "3.16.12", + "effect": "3.18.4", "empathic": "2.0.0" } }, "node_modules/@prisma/debug": { - "version": "6.17.1", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.17.1.tgz", - "integrity": "sha512-Vf7Tt5Wh9XcndpbmeotuqOMLWPTjEKCsgojxXP2oxE1/xYe7PtnP76hsouG9vis6fctX+TxgmwxTuYi/+xc7dQ==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.18.0.tgz", + "integrity": "sha512-PMVPMmxPj0ps1VY75DIrT430MoOyQx9hmm174k6cmLZpcI95rAPXOQ+pp8ANQkJtNyLVDxnxVJ0QLbrm/ViBcg==", "devOptional": true }, "node_modules/@prisma/engines": { - "version": "6.17.1", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.17.1.tgz", - "integrity": "sha512-D95Ik3GYZkqZ8lSR4EyFOJ/tR33FcYRP8kK61o+WMsyD10UfJwd7+YielflHfKwiGodcqKqoraWw8ElAgMDbPw==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.18.0.tgz", + "integrity": "sha512-i5RzjGF/ex6AFgqEe2o1IW8iIxJGYVQJVRau13kHPYEL1Ck8Zvwuzamqed/1iIljs5C7L+Opiz5TzSsUebkriA==", "devOptional": true, "hasInstallScript": true, "dependencies": { - "@prisma/debug": "6.17.1", - "@prisma/engines-version": "6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac", - "@prisma/fetch-engine": "6.17.1", - "@prisma/get-platform": "6.17.1" + "@prisma/debug": "6.18.0", + "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", + "@prisma/fetch-engine": "6.18.0", + "@prisma/get-platform": "6.18.0" } }, "node_modules/@prisma/engines-version": { - "version": "6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac.tgz", - "integrity": "sha512-17140E3huOuD9lMdJ9+SF/juOf3WR3sTJMVyyenzqUPbuH+89nPhSWcrY+Mf7tmSs6HvaO+7S+HkELinn6bhdg==", + "version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f.tgz", + "integrity": "sha512-T7Af4QsJQnSgWN1zBbX+Cha5t4qjHRxoeoWpK4JugJzG/ipmmDMY5S+O0N1ET6sCBNVkf6lz+Y+ZNO9+wFU8pQ==", "devOptional": true }, "node_modules/@prisma/fetch-engine": { - "version": "6.17.1", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.17.1.tgz", - "integrity": "sha512-AYZiHOs184qkDMiTeshyJCtyL4yERkjfTkJiSJdYuSfc24m94lTNL5+GFinZ6vVz+ktX4NJzHKn1zIFzGTWrWg==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.18.0.tgz", + "integrity": "sha512-TdaBvTtBwP3IoqVYoGIYpD4mWlk0pJpjTJjir/xLeNWlwog7Sl3bD2J0jJ8+5+q/6RBg+acb9drsv5W6lqae7A==", "devOptional": true, "dependencies": { - "@prisma/debug": "6.17.1", - "@prisma/engines-version": "6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac", - "@prisma/get-platform": "6.17.1" + "@prisma/debug": "6.18.0", + "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", + "@prisma/get-platform": "6.18.0" } }, "node_modules/@prisma/get-platform": { - "version": "6.17.1", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.17.1.tgz", - "integrity": "sha512-AKEn6fsfz0r482S5KRDFlIGEaq9wLNcgalD1adL+fPcFFblIKs1sD81kY/utrHdqKuVC6E1XSRpegDK3ZLL4Qg==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.18.0.tgz", + "integrity": "sha512-uXNJCJGhxTCXo2B25Ta91Rk1/Nmlqg9p7G9GKh8TPhxvAyXCvMNQoogj4JLEUy+3ku8g59cpyQIKFhqY2xO2bg==", "devOptional": true, "dependencies": { - "@prisma/debug": "6.17.1" + "@prisma/debug": "6.18.0" } }, "node_modules/@scarf/scarf": { @@ -6953,9 +6944,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/effect": { - "version": "3.16.12", - "resolved": "https://registry.npmjs.org/effect/-/effect-3.16.12.tgz", - "integrity": "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", + "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", "devOptional": true, "dependencies": { "@standard-schema/spec": "^1.0.0", @@ -9542,9 +9533,9 @@ "dev": true }, "node_modules/load-esm": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.2.tgz", - "integrity": "sha512-nVAvWk/jeyrWyXEAs84mpQCYccxRqgKY4OznLuJhJCa0XsPSfdOIr2zvBZEj3IHEHbX97jjscKRRV539bW0Gpw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.3.tgz", + "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==", "funding": [ { "type": "github", @@ -10468,11 +10459,12 @@ } }, "node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", - "engines": { - "node": ">=16" + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/path-type": { @@ -10703,14 +10695,14 @@ } }, "node_modules/prisma": { - "version": "6.17.1", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.17.1.tgz", - "integrity": "sha512-ac6h0sM1Tg3zu8NInY+qhP/S9KhENVaw9n1BrGKQVFu05JT5yT5Qqqmb8tMRIE3ZXvVj4xcRA5yfrsy4X7Yy5g==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.18.0.tgz", + "integrity": "sha512-bXWy3vTk8mnRmT+SLyZBQoC2vtV9Z8u7OHvEu+aULYxwiop/CPiFZ+F56KsNRNf35jw+8wcu8pmLsjxpBxAO9g==", "devOptional": true, "hasInstallScript": true, "dependencies": { - "@prisma/config": "6.17.1", - "@prisma/engines": "6.17.1" + "@prisma/config": "6.18.0", + "@prisma/engines": "6.18.0" }, "bin": { "prisma": "build/index.js" diff --git a/package.json b/package.json index c8db806..589f94f 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@nestjs/platform-express": "^11.1.6", "@nestjs/schedule": "^6.0.0", "@nestjs/swagger": "^11.2.0", - "@prisma/client": "^6.17.1", + "@prisma/client": "^6.18.0", "bullmq": "^5.58.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.2", @@ -86,7 +86,7 @@ "globals": "^16.0.0", "jest": "^29.7.0", "prettier": "^3.4.2", - "prisma": "^6.17.1", + "prisma": "^6.18.0", "source-map-support": "^0.5.21", "supertest": "^7.0.0", "ts-jest": "^29.2.5", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 1d62fbd..60503eb 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -24,10 +24,7 @@ model Users { role Roles @default(GUEST) employee Employees? @relation("UserEmployee") - customer Customers? @relation("UserCustomer") oauth_sessions OAuthSessions[] @relation("UserOAuthSessions") - employees_archive EmployeesArchive[] @relation("UsersToEmployeesToArchive") - customer_archive CustomersArchive[] @relation("UserToCustomersToArchive") preferences Preferences? @relation("UserPreferences") @@map("users") @@ -49,61 +46,13 @@ model Employees { crew Employees[] @relation("EmployeeSupervisor") - archive EmployeesArchive[] @relation("EmployeeToArchive") timesheet Timesheets[] @relation("TimesheetEmployee") leave_request LeaveRequests[] @relation("LeaveRequestEmployee") - supervisor_archive EmployeesArchive[] @relation("EmployeeSupervisorToArchive") schedule_presets SchedulePresets[] @relation("SchedulePreset") @@map("employees") } -model EmployeesArchive { - id Int @id @default(autoincrement()) - employee Employees @relation("EmployeeToArchive", fields: [employee_id], references: [id]) - employee_id Int - user_id String @db.Uuid - user Users @relation("UsersToEmployeesToArchive", fields: [user_id], references: [id]) - supervisor Employees? @relation("EmployeeSupervisorToArchive", fields: [supervisor_id], references: [id]) - supervisor_id Int? - - archived_at DateTime @default(now()) - first_name String - last_name String - job_title String? - is_supervisor Boolean - external_payroll_id Int - company_code Int - first_work_day DateTime @db.Date - last_work_day DateTime @db.Date - - @@map("employees_archive") -} - -model Customers { - id Int @id @default(autoincrement()) - user Users @relation("UserCustomer", fields: [user_id], references: [id]) - user_id String @unique @db.Uuid - invoice_id Int? @unique - - archive CustomersArchive[] @relation("CustomerToArchive") - - @@map("customers") -} - -model CustomersArchive { - id Int @id @default(autoincrement()) - customer Customers @relation("CustomerToArchive", fields: [customer_id], references: [id]) - customer_id Int - user Users @relation("UserToCustomersToArchive", fields: [user_id], references: [id]) - user_id String @db.Uuid - - archived_at DateTime @default(now()) - invoice_id Int? @unique - - @@map("customers_archive") -} - model LeaveRequests { id Int @id @default(autoincrement()) employee Employees @relation("LeaveRequestEmployee", fields: [employee_id], references: [id]) @@ -216,13 +165,6 @@ model SchedulePresetShifts { @@map("schedule_preset_shifts") } - - - - - - - model Shifts { id Int @id @default(autoincrement()) timesheet Timesheets @relation("ShiftTimesheet", fields: [timesheet_id], references: [id]) @@ -283,7 +225,7 @@ model Expenses { attachment Int? date DateTime @db.Date - amount Decimal @db.Money + amount Decimal? @db.Decimal(12,2) mileage Decimal? @db.Decimal(12,2) comment String supervisor_comment String? @@ -305,7 +247,7 @@ model ExpensesArchive { archived_at DateTime @default(now()) bank_code_id Int date DateTime @db.Date - amount Decimal? @db.Money + amount Decimal? @db.Decimal(12,2) mileage Decimal? @db.Decimal(12,2) comment String? is_approved Boolean diff --git a/src/modules/expenses/controllers/expense.controller.ts b/src/modules/expenses/controllers/expense.controller.ts index bb37634..e5e5f56 100644 --- a/src/modules/expenses/controllers/expense.controller.ts +++ b/src/modules/expenses/controllers/expense.controller.ts @@ -1,7 +1,8 @@ -import { Body, Controller, Param, ParseIntPipe, Post } from "@nestjs/common"; +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { ExpenseDto } from "../dtos/expense.dto"; -import { CreateResult, ExpenseUpsertService } from "../services/expense-upsert.service"; +import { CreateResult, ExpenseUpsertService, UpdateResult } from "../services/expense-upsert.service"; +import { updateExpenseDto } from "src/modules/expenses/dtos/update-expense.dto"; @Controller('expense') @@ -11,11 +12,21 @@ export class ExpenseController { private readonly upsert_service: ExpenseUpsertService, ){} + @Post(':timesheet_id') + create( + @Param('timesheet_id', ParseIntPipe) timesheet_id: number, + @Body() dto: ExpenseDto): Promise{ + return this.upsert_service.createExpense(timesheet_id, dto); + } - // @Post(':timesheet_id') - // create( - // @Param('timesheet_id', ParseIntPipe) timesheet_id: number, - // @Body() dto: ExpenseDto): Promise{ - // return this.upsert_service.createExpense(timesheet_id, dto); - // } + @Patch() + update( + @Body() body: { update :{ id: number; dto: updateExpenseDto }}): Promise{ + return this.upsert_service.updateExpense(body.update); + } + + @Delete(':expense_id') + remove(@Param('expense_id') expense_id: number) { + return this.upsert_service.deleteExpense(expense_id); + } } \ No newline at end of file diff --git a/src/modules/expenses/dtos/expense.dto.ts b/src/modules/expenses/dtos/expense.dto.ts index 51cc174..7e037e8 100644 --- a/src/modules/expenses/dtos/expense.dto.ts +++ b/src/modules/expenses/dtos/expense.dto.ts @@ -3,9 +3,9 @@ import { IsBoolean, IsInt, IsOptional, IsString, MaxLength } from "class-validat export class ExpenseDto { @IsInt() bank_code_id!: number; @IsInt() timesheet_id!: number; - @IsString() @IsOptional() attachment?: string; + @IsInt() @IsOptional() attachment?: number; - @IsString() date!: string; + @IsString() date!: string; @IsInt() @IsOptional() amount?: number; @IsInt() @IsOptional() mileage?: number; @IsString() @MaxLength(280) comment!: string; diff --git a/src/modules/expenses/dtos/get-expense.dto.ts b/src/modules/expenses/dtos/get-expense.dto.ts index 6c3056e..773a1a7 100644 --- a/src/modules/expenses/dtos/get-expense.dto.ts +++ b/src/modules/expenses/dtos/get-expense.dto.ts @@ -1,7 +1,8 @@ export class GetExpenseDto { + id: number; timesheet_id: number; bank_code_id: number; - attachment?: string; + attachment?: number; date: string; comment: string; mileage?: number; diff --git a/src/modules/expenses/expenses.module.ts b/src/modules/expenses/expenses.module.ts index 490ec9e..85f62cb 100644 --- a/src/modules/expenses/expenses.module.ts +++ b/src/modules/expenses/expenses.module.ts @@ -1,23 +1,15 @@ -// import { ExpensesController } from "./controllers/expenses.controller"; -// import { Module } from "@nestjs/common"; -// import { ExpensesQueryService } from "./services/expenses-query.service"; -// import { BusinessLogicsModule } from "src/modules/business-logics/business-logics.module"; -// import { ExpensesCommandService } from "./services/expenses-command.service"; -// import { ExpensesArchivalService } from "./services/expenses-archival.service"; -// import { SharedModule } from "../shared/shared.module"; +import { ExpensesArchivalService } from "./services/expenses-archival.service"; +import { BusinessLogicsModule } from "src/modules/business-logics/business-logics.module"; +import { ExpenseUpsertService } from "src/modules/expenses/services/expense-upsert.service"; +import { ExpenseController } from "src/modules/expenses/controllers/expense.controller"; +import { SharedModule } from "../shared/shared.module"; +import { Module } from "@nestjs/common"; -// @Module({ -// imports: [BusinessLogicsModule, SharedModule], -// controllers: [ExpensesController], -// providers: [ -// ExpensesQueryService, -// ExpensesArchivalService, -// ExpensesCommandService, -// ], -// exports: [ -// ExpensesQueryService, -// ExpensesArchivalService, -// ], -// }) +@Module({ + imports: [ BusinessLogicsModule, SharedModule ], + controllers: [ ExpenseController ], + providers: [ ExpenseUpsertService, ExpensesArchivalService ], + exports: [ ExpensesArchivalService ], +}) -// export class ExpensesModule {} \ No newline at end of file +export class ExpensesModule {} \ No newline at end of file diff --git a/src/modules/expenses/helpers/expenses-date-time-helpers.ts b/src/modules/expenses/helpers/expenses-date-time-helpers.ts index ce14e69..ef81852 100644 --- a/src/modules/expenses/helpers/expenses-date-time-helpers.ts +++ b/src/modules/expenses/helpers/expenses-date-time-helpers.ts @@ -1,3 +1,5 @@ export const toDateFromString = (ymd: string): Date => { return new Date(`${ymd}T00:00:00:000Z`); -} \ No newline at end of file +} +export const toStringFromDate = (date: Date) => + date.toISOString().slice(0,10); \ No newline at end of file diff --git a/src/modules/expenses/services/expense-upsert.service.ts b/src/modules/expenses/services/expense-upsert.service.ts index 5e47a1e..d2a332c 100644 --- a/src/modules/expenses/services/expense-upsert.service.ts +++ b/src/modules/expenses/services/expense-upsert.service.ts @@ -1,41 +1,171 @@ -import { Injectable } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; -import { GetExpenseDto } from "../dtos/get-expense.dto"; +import { toDateFromString, toStringFromDate } from "../helpers/expenses-date-time-helpers"; +import { Injectable, NotFoundException } from "@nestjs/common"; import { updateExpenseDto } from "../dtos/update-expense.dto"; +import { GetExpenseDto } from "../dtos/get-expense.dto"; +import { PrismaService } from "src/prisma/prisma.service"; import { ExpenseDto } from "../dtos/expense.dto"; -import { toDateFromString } from "../helpers/expenses-date-time-helpers"; -type Normalized = { date: Date; comment: string; supervisor_comment: string; }; +type Normalized = { date: Date; comment: string; supervisor_comment?: string; }; -export type CreateResult = { ok: true; data: GetExpenseDto } | { ok: false; error: any }; +export type CreateResult = { ok: true; data: GetExpenseDto } | { ok: false; error: any }; export type UpdatePayload = { id: number; dto: updateExpenseDto }; -export type UpdateResult = { ok: true; id: number; data: GetExpenseDto } | { ok: false; id: number; error: any }; -export type DeleteResult = { ok: true; id: number; } | { ok: false; id: number; error: any }; - -type NormedOk = { dto: GetExpenseDto; normed: Normalized }; -type NormedErr = { error: any }; +export type UpdateResult = { ok: true; id: number; data: GetExpenseDto } | { ok: false; id: number; error: any }; +export type DeleteResult = { ok: true; id: number; } | { ok: false; id: number; error: any }; @Injectable() export class ExpenseUpsertService { - constructor(private readonly prisma: PrismaService){} + constructor(private readonly prisma: PrismaService) { } //_________________________________________________________________ // CREATE //_________________________________________________________________ - //normalized frontend data to match DB - async createExpense(timesheet_id: number, dto: ExpenseDto){ - const normed_expense = this.normalizeExpenseDto(dto) - + async createExpense(timesheet_id: number, dto: ExpenseDto): Promise { + try { + //normalize strings and dates + const normed_expense = this.normalizeExpenseDto(dto); + //parse numbers + const parsed_amount = this.parseOptionalNumber(dto.amount, "amount"); + const parsed_mileage = this.parseOptionalNumber(dto.mileage, "mileage"); + const parsed_attachment = this.parseOptionalNumber(dto.attachment, "attachment"); + + //create a new expense + const expense = await this.prisma.expenses.create({ + data: { + timesheet_id, + bank_code_id: dto.bank_code_id, + attachment: parsed_attachment, + date: normed_expense.date, + amount: parsed_amount, + mileage: parsed_mileage, + comment: normed_expense.comment, + supervisor_comment: normed_expense.supervisor_comment, + is_approved: dto.is_approved, + }, + //return the newly created expense with id + select: { + id: true, + timesheet_id: true, + bank_code_id: true, + attachment: true, + date: true, + amount: true, + mileage: true, + comment: true, + supervisor_comment: true, + is_approved: true, + }, + }); + + //build an object to return to the frontend to display + const created: GetExpenseDto = { + id: expense.id, + timesheet_id: expense.timesheet_id, + bank_code_id: expense.bank_code_id, + attachment: expense.attachment ?? undefined, + date: toStringFromDate(expense.date), + amount: expense.amount?.toNumber(), + mileage: expense.mileage?.toNumber(), + comment: expense.comment, + supervisor_comment: expense.supervisor_comment ?? undefined, + is_approved: expense.is_approved, + }; + return { ok: true, data: created } + + } catch (error) { + return { ok: false, error: error } + } + } + + //_________________________________________________________________ + // UPDATE + //_________________________________________________________________ + async updateExpense({id, dto}: UpdatePayload): Promise { + try { + //checks for modifications + const data: Record = {}; + if (dto.date !== undefined) data.date = toDateFromString(dto.date); + if (dto.comment !== undefined) data.comment = this.truncate280(dto.comment); + if (dto.attachment !== undefined) data.attachment = this.parseOptionalNumber(dto.attachment, "attachment"); + if (dto.amount !== undefined) data.amount = this.parseOptionalNumber(dto.amount, "amount"); + if (dto.mileage !== undefined) data.mileage = this.parseOptionalNumber(dto.mileage, "mileage"); + if (dto.bank_code_id !== undefined) data.bank_code_id = dto.bank_code_id; + if (dto.supervisor_comment !== undefined) { + data.supervisor_comment = dto.supervisor_comment?.trim() + ? this.truncate280(dto.supervisor_comment.trim()) + : null; + } + //return an error if no fields needs an update + if(!Object.keys(data).length) { + return { ok: false, id, error: new Error("Nothing to update")}; + } + + const expense = await this.prisma.expenses.update({ + where: { id }, + data, + select: { + id: true, + timesheet_id: true, + bank_code_id: true, + attachment: true, + date: true, + amount: true, + mileage: true, + comment: true, + supervisor_comment: true, + is_approved: true, + }, + }); + + const updated: GetExpenseDto = { + id: expense.id, + timesheet_id: expense.timesheet_id, + bank_code_id: expense.bank_code_id, + attachment: expense.attachment ?? undefined, + date: toStringFromDate(expense.date), + amount: expense.amount?.toNumber(), + mileage: expense.mileage?.toNumber(), + comment: expense.comment, + supervisor_comment: expense.supervisor_comment ?? undefined, + is_approved: expense.is_approved, + }; + return { ok: true, id: expense.id, data: updated }; + } catch (error) { + return { ok: false, id: id, error: error} + } + } + //_________________________________________________________________ + // DELETE + //_________________________________________________________________ + async deleteExpense(expense_id: number): Promise { + try { + await this.prisma.$transaction(async (tx) => { + const expense = await tx.expenses.findUnique({ + where: { id: expense_id }, + select: { id: true }, + }); + if(!expense) throw new NotFoundException(`Expense with id: ${expense_id} not found`); + + await tx.expenses.delete({ where: { id: expense_id }}); + return { success: true }; + }); + return { ok: true, id: expense_id }; + } catch (error) { + return { ok: false, id: expense_id, error }; + } } //_________________________________________________________________ // LOCAL HELPERS //_________________________________________________________________ + //makes sure that comments are the right length the date is of Date type private normalizeExpenseDto(dto: ExpenseDto): Normalized { - const date = toDateFromString(dto.date); + const date = toDateFromString(dto.date); const comment = this.truncate280(dto.comment); - const supervisor_comment = this.truncate280(dto.supervisor_comment? dto.supervisor_comment : ''); + const supervisor_comment = + dto.supervisor_comment && dto.supervisor_comment.trim() + ? this.truncate280(dto.supervisor_comment.trim()) + : undefined; return { date, comment, supervisor_comment }; } @@ -43,4 +173,12 @@ export class ExpenseUpsertService { private truncate280 = (input: string): string => { return input.length > 280 ? input.slice(0, 280) : input; } + + //makes sure that the type of data of numeric values is valid + private parseOptionalNumber = (value: unknown, field: string) => { + if (value == null) return undefined; + const parsed = Number(value); + if (Number.isNaN(parsed)) throw new Error(`Invalid value : ${value} for ${field}`); + return parsed; + }; } \ No newline at end of file diff --git a/src/modules/shifts/controllers/shift.controller.ts b/src/modules/shifts/controllers/shift.controller.ts index bffa9cb..b35766d 100644 --- a/src/modules/shifts/controllers/shift.controller.ts +++ b/src/modules/shifts/controllers/shift.controller.ts @@ -12,12 +12,12 @@ export class ShiftController { private readonly get_service: ShiftsGetService ){} - @Get() - async getShiftsByIds( - @Query("shift_ids") shift_ids: string) { - const parsed = shift_ids.split(/,\s*/).map(value => Number(value)).filter(Number.isFinite); - return this.get_service.getShiftByShiftId(parsed); - } + // @Get() + // async getShiftsByIds( + // @Query("shift_ids") shift_ids: string) { + // const parsed = shift_ids.split(/,\s*/).map(value => Number(value)).filter(Number.isFinite); + // return this.get_service.getShiftByShiftId(parsed); + // } @Post(':timesheet_id') createBatch( diff --git a/src/modules/shifts/services/shifts-upsert.service.ts b/src/modules/shifts/services/shifts-upsert.service.ts index e63380b..cc0e790 100644 --- a/src/modules/shifts/services/shifts-upsert.service.ts +++ b/src/modules/shifts/services/shifts-upsert.service.ts @@ -336,20 +336,20 @@ export class ShiftsUpsertService { //finds shifts using shit_ids //recalc overtime shifts after delete //blocs deletion if approved - async deleteShift(shift_id: number) { - return await this.prisma.$transaction(async (tx) =>{ + async deleteShift(shift_id: number) { + return await this.prisma.$transaction(async (tx) => { const shift = await tx.shifts.findUnique({ - where: { id: shift_id }, + where: { id: shift_id }, select: { id: true, date: true, timesheet_id: true }, }); - if(!shift) throw new NotFoundException(`Shift with id #${shift_id} not found`); + if (!shift) throw new NotFoundException(`Shift with id #${shift_id} not found`); await tx.shifts.delete({ where: { id: shift_id } }); - const summary = await this.overtime.getWeekOvertimeSummary( shift.timesheet_id, shift.date, tx); - return { - success: true, - overtime: summary + const summary = await this.overtime.getWeekOvertimeSummary(shift.timesheet_id, shift.date, tx); + return { + success: true, + overtime: summary }; }); } From 1289aed720c7421d57112effa2de1faf3802c6eb Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 24 Oct 2025 16:38:18 -0400 Subject: [PATCH 04/57] reafactor(time-and-attendance): organized files and folders and centralize module files --- docs/swagger/swagger-spec.json | 505 +++++------------- prisma/mock-seeds-scripts/04-customers.ts | 46 +- .../05-employees-archive.ts | 76 +-- .../06-customers-archive.ts | 56 +- .../08-leave-requests-archive.ts | 148 ++--- .../mock-seeds-scripts/11-shifts-archive.ts | 122 ++--- .../mock-seeds-scripts/13-expenses-archive.ts | 108 ++-- src/app.module.ts | 27 +- .../employees/employees.module.ts | 13 + .../employees-archive.controller.ts | 58 +- .../expenses-archive.controller.ts | 2 +- .../controllers/shifts-archive.controller.ts | 2 +- .../timesheets-archive.controller.ts | 2 +- .../archival/services/archival.service.ts | 6 +- .../controllers/customers.controller.ts | 122 ++--- src/modules/customers/customers.module.ts | 16 +- .../controllers/employees.controller.ts | 178 +++--- src/modules/employees/employees.module.ts | 23 +- .../services/employees-archival.service.ts | 308 +++++------ src/modules/expenses/expenses.module.ts | 15 - src/modules/exports/csv-exports.module.ts | 2 +- src/modules/preferences/preferences.module.ts | 2 +- .../services/preferences.service.ts | 2 +- src/modules/timesheets/timesheets.module.ts | 23 - .../domains/business-logics.module.ts | 0 .../domains/services/after-hours.service.ts | 0 .../domains/services/holiday.service.ts | 0 .../domains/services/mileage.service.ts | 0 .../domains/services/overtime.service.ts | 0 .../domains/services/sick-leave.service.ts | 0 .../domains/services/vacation.service.ts | 0 .../controllers/expense.controller.ts | 2 +- .../modules/expenses/dtos/expense.dto.ts | 0 .../modules/expenses/dtos/get-expense.dto.ts | 0 .../expenses/dtos/update-expense.dto.ts | 0 .../modules/expenses/expenses.module.ts | 15 + .../helpers/expenses-date-time-helpers.ts | 0 .../services/expense-upsert.service.ts | 0 .../services/expenses-archival.service.ts | 0 .../create-expense.dto.ts | 0 .../expenses-command.service.ts | 0 .../expenses-query.service.ts | 0 .../expenses.controller.ts | 0 .../expenses.types.interfaces.ts | 0 .../~misc_deprecated-files/expenses.utils.ts | 0 .../search-expense.dto.ts | 0 .../update-expense.dto.ts | 0 .../upsert-expense.dto.ts | 0 .../controllers/leave-requests.controller.ts | 0 .../dtos/leave-request-view.dto.ts | 0 .../dtos/upsert-leave-request.dto.ts | 0 .../leave-requests/leave-requests.module.ts | 0 .../mappers/leave-requests-archive.mapper.ts | 0 .../mappers/leave-requests.mapper.ts | 0 .../holiday-leave-requests.service.ts | 0 .../services/leave-request.service.ts | 0 .../services/sick-leave-requests.service.ts | 0 .../vacation-leave-requests.service.ts | 0 .../utils/leave-request.transform.ts | 0 .../utils/leave-request.util.ts | 0 .../utils/leave-requests-archive.select.ts | 0 .../utils/leave-requests.select.ts | 0 .../controllers/pay-periods.controller.ts | 0 .../pay-period/dtos/bulk-crew-approval.dto.ts | 0 .../pay-period/dtos/bundle-pay-period.dto.ts | 0 .../dtos/overview-employee-period.dto.ts | 0 .../dtos/overview-pay-period.dto.ts | 0 .../modules/pay-period/dtos/pay-period.dto.ts | 0 .../pay-period/mappers/pay-periods.mapper.ts | 0 .../modules/pay-period/pay-periods.module.ts | 13 +- .../services/pay-periods-command.service.ts | 0 .../services/pay-periods-query.service.ts | 0 .../modules/pay-period/utils/pay-year.util.ts | 0 .../shared/constants/date-time.constant.ts | 0 .../shared/constants/regex.constant.ts | 0 .../shared/constants/utils.constant.ts | 0 .../shared/helpers/date-time.helpers.ts | 0 .../shared/interfaces/shifts.interface.ts | 0 .../modules/shared/selects/expenses.select.ts | 0 .../shared/selects/pay-periods.select.ts | 0 .../modules/shared/selects/shifts.select.ts | 0 .../modules/shared/shared.module.ts | 0 .../shared/types/upsert-actions.types.ts | 0 .../utils/resolve-bank-type-id.utils.ts | 0 .../shared/utils/resolve-email-id.utils.ts | 0 .../shared/utils/resolve-full-name.utils.ts | 0 .../shared/utils/resolve-shifts-id.utils.ts | 0 .../shared/utils/resolve-timesheet.utils.ts | 2 +- .../controller/schedule-presets.controller.ts | 2 +- .../dtos/create-schedule-preset-shifts.dto.ts | 0 .../dtos/create-schedule-presets.dto.ts | 0 .../mappers/schedule-presets.mappers.ts | 0 .../schedule-presets.module.ts | 2 +- .../schedule-presets-apply.service.ts | 2 +- .../schedule-presets-command.service.ts | 6 +- .../schedule-presets-query.service.ts | 2 +- .../types/schedule-presets.types.ts | 0 .../shifts/controllers/shift.controller.ts | 0 .../time-tracker/shifts/dtos/get-shift.dto.ts | 0 .../time-tracker/shifts/dtos/shift.dto.ts | 0 .../shifts/dtos/update-shift.dto.ts | 0 .../helpers/shifts-date-time-helpers.ts | 0 .../services/shifts-archival.service.ts | 0 .../shifts/services/shifts-get.service.ts | 0 .../shifts/services/shifts-upsert.service.ts | 2 +- .../time-tracker/shifts/shifts.module.ts | 2 +- .../get-shift-overview.dto.ts | 0 .../shifts-command.service.ts | 0 .../shifts-overview-row.interface.ts | 0 .../shifts-query.service.ts | 0 .../shifts-upsert.types.ts | 0 .../shifts.controller.ts | 0 .../~misc_deprecated-files/shifts.helpers.ts | 0 .../~misc_deprecated-files/shifts.utils.ts | 0 .../upsert-shift.dto.ts | 0 .../controllers/timesheet.controller.ts | 2 +- .../timesheets/dtos/timesheet.dto.ts | 0 .../helpers/timesheets-date-time-helpers.ts | 0 .../services/timesheet-approval.service.ts | 0 .../services/timesheet-archive.service.ts | 0 .../timesheet-get-overview.service.ts | 0 .../timesheets/timesheets.module.ts | 8 +- .../create-timesheet.dto.ts | 0 .../search-timesheet.dto.ts | 0 .../timesheet-period.dto.ts | 0 .../timesheet.helpers.ts | 0 .../timesheet.mappers.ts | 0 .../timesheet.selectors.ts | 0 .../~misc_deprecated-files/timesheet.types.ts | 0 .../~misc_deprecated-files/timesheet.utils.ts | 0 .../timesheets-command.service.ts | 0 .../timesheets-query.service.ts | 0 .../timesheets.controller.ts | 0 .../time-and-attendance.module.ts | 42 ++ 134 files changed, 880 insertions(+), 1084 deletions(-) create mode 100644 src/identity-and-account/employees/employees.module.ts delete mode 100644 src/modules/expenses/expenses.module.ts delete mode 100644 src/modules/timesheets/timesheets.module.ts rename src/{Time_And_Attendance => time-and-attendance}/domains/business-logics.module.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/domains/services/after-hours.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/domains/services/holiday.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/domains/services/mileage.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/domains/services/overtime.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/domains/services/sick-leave.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/domains/services/vacation.service.ts (100%) rename src/{ => time-and-attendance}/modules/expenses/controllers/expense.controller.ts (91%) rename src/{ => time-and-attendance}/modules/expenses/dtos/expense.dto.ts (100%) rename src/{ => time-and-attendance}/modules/expenses/dtos/get-expense.dto.ts (100%) rename src/{ => time-and-attendance}/modules/expenses/dtos/update-expense.dto.ts (100%) create mode 100644 src/time-and-attendance/modules/expenses/expenses.module.ts rename src/{ => time-and-attendance}/modules/expenses/helpers/expenses-date-time-helpers.ts (100%) rename src/{ => time-and-attendance}/modules/expenses/services/expense-upsert.service.ts (100%) rename src/{ => time-and-attendance}/modules/expenses/services/expenses-archival.service.ts (100%) rename src/{ => time-and-attendance}/modules/expenses/~misc_deprecated-files/create-expense.dto.ts (100%) rename src/{ => time-and-attendance}/modules/expenses/~misc_deprecated-files/expenses-command.service.ts (100%) rename src/{ => time-and-attendance}/modules/expenses/~misc_deprecated-files/expenses-query.service.ts (100%) rename src/{ => time-and-attendance}/modules/expenses/~misc_deprecated-files/expenses.controller.ts (100%) rename src/{ => time-and-attendance}/modules/expenses/~misc_deprecated-files/expenses.types.interfaces.ts (100%) rename src/{ => time-and-attendance}/modules/expenses/~misc_deprecated-files/expenses.utils.ts (100%) rename src/{ => time-and-attendance}/modules/expenses/~misc_deprecated-files/search-expense.dto.ts (100%) rename src/{ => time-and-attendance}/modules/expenses/~misc_deprecated-files/update-expense.dto.ts (100%) rename src/{ => time-and-attendance}/modules/expenses/~misc_deprecated-files/upsert-expense.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/leave-requests/controllers/leave-requests.controller.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/leave-requests/dtos/leave-request-view.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/leave-requests/dtos/upsert-leave-request.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/leave-requests/leave-requests.module.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/leave-requests/mappers/leave-requests-archive.mapper.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/leave-requests/mappers/leave-requests.mapper.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/leave-requests/services/holiday-leave-requests.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/leave-requests/services/leave-request.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/leave-requests/services/sick-leave-requests.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/leave-requests/services/vacation-leave-requests.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/leave-requests/utils/leave-request.transform.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/leave-requests/utils/leave-request.util.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/leave-requests/utils/leave-requests-archive.select.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/leave-requests/utils/leave-requests.select.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/pay-period/controllers/pay-periods.controller.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/pay-period/dtos/bulk-crew-approval.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/pay-period/dtos/bundle-pay-period.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/pay-period/dtos/overview-employee-period.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/pay-period/dtos/overview-pay-period.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/pay-period/dtos/pay-period.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/pay-period/mappers/pay-periods.mapper.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/pay-period/pay-periods.module.ts (68%) rename src/{Time_And_Attendance => time-and-attendance}/modules/pay-period/services/pay-periods-command.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/pay-period/services/pay-periods-query.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/pay-period/utils/pay-year.util.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/constants/date-time.constant.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/constants/regex.constant.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/constants/utils.constant.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/helpers/date-time.helpers.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/interfaces/shifts.interface.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/selects/expenses.select.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/selects/pay-periods.select.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/selects/shifts.select.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/shared.module.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/types/upsert-actions.types.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/utils/resolve-bank-type-id.utils.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/utils/resolve-email-id.utils.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/utils/resolve-full-name.utils.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/utils/resolve-shifts-id.utils.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/shared/utils/resolve-timesheet.utils.ts (94%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts (96%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/schedule-presets/mappers/schedule-presets.mappers.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/schedule-presets/schedule-presets.module.ts (91%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts (98%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts (98%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts (96%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/schedule-presets/types/schedule-presets.types.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/controllers/shift.controller.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/dtos/get-shift.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/dtos/shift.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/dtos/update-shift.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/helpers/shifts-date-time-helpers.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/services/shifts-archival.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/services/shifts-get.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/services/shifts-upsert.service.ts (99%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/shifts.module.ts (89%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/~misc_deprecated-files/get-shift-overview.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/~misc_deprecated-files/shifts-command.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/~misc_deprecated-files/shifts-overview-row.interface.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/~misc_deprecated-files/shifts-query.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/~misc_deprecated-files/shifts-upsert.types.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/~misc_deprecated-files/shifts.controller.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/~misc_deprecated-files/shifts.helpers.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/~misc_deprecated-files/shifts.utils.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/shifts/~misc_deprecated-files/upsert-shift.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/controllers/timesheet.controller.ts (94%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/dtos/timesheet.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/helpers/timesheets-date-time-helpers.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/services/timesheet-approval.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/services/timesheet-archive.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/timesheets.module.ts (70%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/~misc_deprecated-files/create-timesheet.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/~misc_deprecated-files/search-timesheet.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet-period.dto.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.helpers.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.mappers.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.selectors.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.types.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.utils.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-command.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-query.service.ts (100%) rename src/{Time_And_Attendance => time-and-attendance}/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets.controller.ts (100%) create mode 100644 src/time-and-attendance/time-and-attendance.module.ts diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 15ab59d..0462102 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -71,142 +71,6 @@ ] } }, - "/employees/employee-list": { - "get": { - "operationId": "EmployeesController_findListEmployees", - "parameters": [], - "responses": { - "200": { - "description": "List of employees with scoped info found", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EmployeeListItemDto" - } - } - } - } - }, - "400": { - "description": "List of employees with scoped info not found" - } - }, - "security": [ - { - "access-token": [] - } - ], - "summary": "Find all employees with scoped info", - "tags": [ - "Employees" - ] - } - }, - "/employees/{email}": { - "patch": { - "operationId": "EmployeesController_updateOrArchiveOrRestore", - "parameters": [ - { - "name": "email", - "required": true, - "in": "path", - "description": "Email of the employee", - "schema": { - "type": "number" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateEmployeeDto" - } - } - } - }, - "responses": { - "200": { - "description": "Employee updated or restored", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateEmployeeDto" - } - } - } - }, - "202": { - "description": "Employee archived successfully", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateEmployeeDto" - } - } - } - }, - "404": { - "description": "Employee not found in active or archive" - } - }, - "security": [ - { - "access-token": [] - }, - { - "access-token": [] - } - ], - "summary": "Update, archive or restore an employee", - "tags": [ - "Employees" - ] - } - }, - "/employees/profile/{email}": { - "get": { - "operationId": "EmployeesController_findOneProfile", - "parameters": [ - { - "name": "email", - "required": true, - "in": "path", - "description": "Identifier of the employee", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Employee profile found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EmployeeProfileItemDto" - } - } - } - }, - "400": { - "description": "Employee profile not found" - } - }, - "security": [ - { - "access-token": [] - } - ], - "summary": "Find employee profile", - "tags": [ - "Employees" - ] - } - }, "/notifications/summary": { "get": { "operationId": "NotificationsController_summary", @@ -649,6 +513,39 @@ ] } }, + "/preferences/{email}": { + "patch": { + "operationId": "PreferencesController_updatePreferences", + "parameters": [ + { + "name": "email", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PreferencesDto" + } + } + } + }, + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Preferences" + ] + } + }, "/timesheets": { "get": { "operationId": "TimesheetController_getTimesheetByIds", @@ -688,41 +585,6 @@ ] } }, - "/shift": { - "get": { - "operationId": "ShiftController_getShiftsByIds", - "parameters": [ - { - "name": "shift_ids", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "Shift" - ] - }, - "patch": { - "operationId": "ShiftController_updateBatch", - "parameters": [], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "Shift" - ] - } - }, "/shift/{timesheet_id}": { "post": { "operationId": "ShiftController_createBatch", @@ -759,6 +621,20 @@ ] } }, + "/shift": { + "patch": { + "operationId": "ShiftController_updateBatch", + "parameters": [], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Shift" + ] + } + }, "/shift/{shift_id}": { "delete": { "operationId": "ShiftController_remove", @@ -782,39 +658,6 @@ ] } }, - "/preferences/{email}": { - "patch": { - "operationId": "PreferencesController_updatePreferences", - "parameters": [ - { - "name": "email", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PreferencesDto" - } - } - } - }, - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "Preferences" - ] - } - }, "/schedule-presets/{email}": { "put": { "operationId": "SchedulePresetsController_upsert", @@ -915,6 +758,100 @@ "SchedulePresets" ] } + }, + "/expense/{timesheet_id}": { + "post": { + "operationId": "ExpenseController_create", + "parameters": [ + { + "name": "timesheet_id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExpenseDto" + } + } + } + }, + "responses": { + "201": { + "description": "" + } + }, + "tags": [ + "Expense" + ] + } + }, + "/expense": { + "patch": { + "operationId": "ExpenseController_update", + "parameters": [], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Expense" + ] + } + }, + "/expense/{expense_id}": { + "delete": { + "operationId": "ExpenseController_remove", + "parameters": [ + { + "name": "expense_id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Expense" + ] + } + }, + "/archives/expenses": { + "get": { + "operationId": "ExpensesArchiveController_findOneArchived", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "Archived expense found" + } + }, + "summary": "Fetch expense in archives with its Id", + "tags": [ + "Expense Archives" + ] + } } }, "info": { @@ -974,162 +911,6 @@ } }, "schemas": { - "EmployeeListItemDto": { - "type": "object", - "properties": {} - }, - "UpdateEmployeeDto": { - "type": "object", - "properties": { - "id": { - "type": "number", - "example": 1, - "description": "Unique ID of an employee(primary-key, auto-incremented)" - }, - "user_id": { - "type": "string", - "example": "0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d", - "description": "UUID of the user linked to that employee" - }, - "first_name": { - "type": "string", - "example": "Frodo", - "description": "Employee`s first name" - }, - "last_name": { - "type": "string", - "example": "Baggins", - "description": "Employee`s last name" - }, - "email": { - "type": "string", - "example": "i_cant_do_this_sam@targointernet.com", - "description": "Employee`s email" - }, - "phone_number": { - "type": "string", - "example": "82538437464", - "description": "Employee`s phone number" - }, - "residence": { - "type": "string", - "example": "1 Bagshot Row, Hobbiton, The Shire, Middle-earth", - "description": "Employee`s residence" - }, - "external_payroll_id": { - "type": "number", - "example": 7464, - "description": "external ID for the pay system" - }, - "company_code": { - "type": "number", - "example": 335567447, - "description": "Employee`s company code" - }, - "job_title": { - "type": "string", - "example": "technicient", - "description": "employee`s job title" - }, - "first_work_day": { - "format": "date-time", - "type": "string", - "example": "23/09/3018", - "description": "New hire date or undefined" - }, - "last_work_day": { - "format": "date-time", - "type": "string", - "example": "25/03/3019", - "description": "Termination date (null to restore)" - }, - "supervisor_id": { - "type": "number", - "description": "Supervisor ID" - } - } - }, - "CreateEmployeeDto": { - "type": "object", - "properties": { - "id": { - "type": "number", - "example": 1, - "description": "Unique ID of an employee(primary-key, auto-incremented)" - }, - "user_id": { - "type": "string", - "example": "0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d", - "description": "UUID of the user linked to that employee" - }, - "first_name": { - "type": "string", - "example": "Frodo", - "description": "Employee`s first name" - }, - "last_name": { - "type": "string", - "example": "Baggins", - "description": "Employee`s last name" - }, - "email": { - "type": "string", - "example": "i_cant_do_this_sam@targointernet.com", - "description": "Employee`s email" - }, - "phone_number": { - "type": "string", - "example": "82538437464", - "description": "Employee`s phone number" - }, - "residence": { - "type": "string", - "example": "1 Bagshot Row, Hobbiton, The Shire, Middle-earth", - "description": "Employee`s residence" - }, - "external_payroll_id": { - "type": "number", - "example": 7464, - "description": "external ID for the pay system" - }, - "company_code": { - "type": "number", - "example": 335567447, - "description": "Employee`s company code" - }, - "job_title": { - "type": "string", - "example": "technicient", - "description": "employee`s job title" - }, - "first_work_day": { - "type": "string", - "example": "23/09/3018", - "description": "Employee`s first working day" - }, - "last_work_day": { - "type": "string", - "example": "25/03/3019", - "description": "Employee`s last working day" - } - }, - "required": [ - "id", - "user_id", - "first_name", - "last_name", - "email", - "phone_number", - "external_payroll_id", - "company_code", - "job_title", - "first_work_day" - ] - }, - "EmployeeProfileItemDto": { - "type": "object", - "properties": {} - }, "CreateOauthSessionDto": { "type": "object", "properties": { @@ -1407,6 +1188,10 @@ "SchedulePresetsDto": { "type": "object", "properties": {} + }, + "ExpenseDto": { + "type": "object", + "properties": {} } } } diff --git a/prisma/mock-seeds-scripts/04-customers.ts b/prisma/mock-seeds-scripts/04-customers.ts index aaa63b6..b8319cf 100644 --- a/prisma/mock-seeds-scripts/04-customers.ts +++ b/prisma/mock-seeds-scripts/04-customers.ts @@ -1,28 +1,28 @@ -import { PrismaClient, Roles } from '@prisma/client'; +// import { PrismaClient, Roles } from '@prisma/client'; -const prisma = new PrismaClient(); +// const prisma = new PrismaClient(); -async function main() { - const customerUsers = await prisma.users.findMany({ - where: { role: Roles.CUSTOMER }, - orderBy: { email: 'asc' }, - }); +// async function main() { +// const customerUsers = await prisma.users.findMany({ +// where: { role: Roles.CUSTOMER }, +// orderBy: { email: 'asc' }, +// }); - let i = 0; - for (const u of customerUsers) { - await prisma.customers.upsert({ - where: { user_id: u.id }, - update: {}, - create: { - user_id: u.id, - invoice_id: i % 2 === 0 ? 100000 + i : null, // 1 sur 2 a un invoice_id - }, - }); - i++; - } +// let i = 0; +// for (const u of customerUsers) { +// await prisma.customers.upsert({ +// where: { user_id: u.id }, +// update: {}, +// create: { +// user_id: u.id, +// invoice_id: i % 2 === 0 ? 100000 + i : null, // 1 sur 2 a un invoice_id +// }, +// }); +// i++; +// } - const total = await prisma.customers.count(); - console.log(`✓ Customers: ${total} rows`); -} +// const total = await prisma.customers.count(); +// console.log(`✓ Customers: ${total} rows`); +// } -main().finally(() => prisma.$disconnect()); +// main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/05-employees-archive.ts b/prisma/mock-seeds-scripts/05-employees-archive.ts index 1687b23..b352a7f 100644 --- a/prisma/mock-seeds-scripts/05-employees-archive.ts +++ b/prisma/mock-seeds-scripts/05-employees-archive.ts @@ -1,45 +1,45 @@ -import { PrismaClient } from '@prisma/client'; +// import { PrismaClient } from '@prisma/client'; -if (process.env.SKIP_LEAVE_REQUESTS === 'true') { - console.log("â­ Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)"); - process.exit(0); -} +// if (process.env.SKIP_LEAVE_REQUESTS === 'true') { +// console.log("â­ Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)"); +// process.exit(0); +// } -const prisma = new PrismaClient(); +// const prisma = new PrismaClient(); -function daysAgo(n: number) { - const d = new Date(); - d.setDate(d.getDate() - n); - d.setHours(0,0,0,0); - return d; -} +// function daysAgo(n: number) { +// const d = new Date(); +// d.setDate(d.getDate() - n); +// d.setHours(0,0,0,0); +// return d; +// } -async function main() { - const employees = await prisma.employees.findMany({ - include: { user: true }, - take: 10, // archive 10 - }); +// async function main() { +// const employees = await prisma.employees.findMany({ +// include: { user: true }, +// take: 10, // archive 10 +// }); - for (const e of employees) { - await prisma.employeesArchive.create({ - data: { - employee_id: e.id, - user_id: e.user_id, - first_name: e.user.first_name, - last_name: e.user.last_name, - external_payroll_id: e.external_payroll_id, - company_code: e.company_code, - first_work_day: e.first_work_day, - last_work_day: daysAgo(30), - supervisor_id: e.supervisor_id ?? null, - job_title: e.job_title, - is_supervisor: e.is_supervisor, - }, - }); - } +// for (const e of employees) { +// await prisma.employeesArchive.create({ +// data: { +// employee_id: e.id, +// user_id: e.user_id, +// first_name: e.user.first_name, +// last_name: e.user.last_name, +// external_payroll_id: e.external_payroll_id, +// company_code: e.company_code, +// first_work_day: e.first_work_day, +// last_work_day: daysAgo(30), +// supervisor_id: e.supervisor_id ?? null, +// job_title: e.job_title, +// is_supervisor: e.is_supervisor, +// }, +// }); +// } - const total = await prisma.employeesArchive.count(); - console.log(`✓ EmployeesArchive: ${total} rows (added 10)`); -} +// const total = await prisma.employeesArchive.count(); +// console.log(`✓ EmployeesArchive: ${total} rows (added 10)`); +// } -main().finally(() => prisma.$disconnect()); +// main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/06-customers-archive.ts b/prisma/mock-seeds-scripts/06-customers-archive.ts index 31a7d01..0c2a382 100644 --- a/prisma/mock-seeds-scripts/06-customers-archive.ts +++ b/prisma/mock-seeds-scripts/06-customers-archive.ts @@ -1,35 +1,35 @@ -// prisma/mock-seeds-scripts/06-customers-archive.ts -import { PrismaClient } from '@prisma/client'; +// // prisma/mock-seeds-scripts/06-customers-archive.ts +// import { PrismaClient } from '@prisma/client'; -if (process.env.SKIP_LEAVE_REQUESTS === 'true') { - console.log("â­ Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)"); - process.exit(0); -} +// if (process.env.SKIP_LEAVE_REQUESTS === 'true') { +// console.log("â­ Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)"); +// process.exit(0); +// } -const prisma = new PrismaClient(); +// const prisma = new PrismaClient(); -async function main() { - const customers = await prisma.customers.findMany({ - orderBy: { id: 'asc' }, - take: 5, - }); +// async function main() { +// const customers = await prisma.customers.findMany({ +// orderBy: { id: 'asc' }, +// take: 5, +// }); - for (const c of customers) { - const invoiceId = 200000 + c.id; // déterministe, stable entre runs +// for (const c of customers) { +// const invoiceId = 200000 + c.id; // déterministe, stable entre runs - await prisma.customersArchive.upsert({ - where: { invoice_id: invoiceId }, // invoice_id est unique - update: {}, // idempotent - create: { - customer_id: c.id, - user_id: c.user_id, - invoice_id: invoiceId, - }, - }); - } +// await prisma.customersArchive.upsert({ +// where: { invoice_id: invoiceId }, // invoice_id est unique +// update: {}, // idempotent +// create: { +// customer_id: c.id, +// user_id: c.user_id, +// invoice_id: invoiceId, +// }, +// }); +// } - const total = await prisma.customersArchive.count(); - console.log(`✓ CustomersArchive upserted for ${customers.length} customers (total=${total})`); -} +// const total = await prisma.customersArchive.count(); +// console.log(`✓ CustomersArchive upserted for ${customers.length} customers (total=${total})`); +// } -main().finally(() => prisma.$disconnect()); +// main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/08-leave-requests-archive.ts b/prisma/mock-seeds-scripts/08-leave-requests-archive.ts index 45b1d43..0170635 100644 --- a/prisma/mock-seeds-scripts/08-leave-requests-archive.ts +++ b/prisma/mock-seeds-scripts/08-leave-requests-archive.ts @@ -1,87 +1,87 @@ -import { PrismaClient, LeaveApprovalStatus, LeaveTypes } from '@prisma/client'; +// import { PrismaClient, LeaveApprovalStatus, LeaveTypes } from '@prisma/client'; -if (process.env.SKIP_LEAVE_REQUESTS === 'true') { - console.log('?? Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)'); - process.exit(0); -} +// if (process.env.SKIP_LEAVE_REQUESTS === 'true') { +// console.log('?? Seed leave-requests ignor� (SKIP_LEAVE_REQUESTS=true)'); +// process.exit(0); +// } -const prisma = new PrismaClient(); +// const prisma = new PrismaClient(); -function daysAgo(n: number) { - const d = new Date(); - d.setUTCDate(d.getUTCDate() - n); - d.setUTCHours(0, 0, 0, 0); - return d; -} +// function daysAgo(n: number) { +// const d = new Date(); +// d.setUTCDate(d.getUTCDate() - n); +// d.setUTCHours(0, 0, 0, 0); +// return d; +// } -async function main() { - const employees = await prisma.employees.findMany({ select: { id: true } }); - if (!employees.length) { - throw new Error('Aucun employé trouvé. Exécute le seed employees avant celui-ci.'); - } +// async function main() { +// const employees = await prisma.employees.findMany({ select: { id: true } }); +// if (!employees.length) { +// throw new Error('Aucun employ� trouv�. Ex�cute le seed employees avant celui-ci.'); +// } - const leaveCodes = await prisma.bankCodes.findMany({ - where: { type: { in: ['SICK', 'VACATION', 'HOLIDAY'] } }, - select: { id: true, type: true }, - }); - if (!leaveCodes.length) { - throw new Error("Aucun bank code trouvé avec type in ('SICK','VACATION','HOLIDAY'). Vérifie ta table bank_codes."); - } +// const leaveCodes = await prisma.bankCodes.findMany({ +// where: { type: { in: ['SICK', 'VACATION', 'HOLIDAY'] } }, +// select: { id: true, type: true }, +// }); +// if (!leaveCodes.length) { +// throw new Error("Aucun bank code trouv� avec type in ('SICK','VACATION','HOLIDAY'). V�rifie ta table bank_codes."); +// } - const statuses = Object.values(LeaveApprovalStatus); - const created = [] as Array<{ id: number; employee_id: number; leave_type: LeaveTypes; date: Date; comment: string; approval_status: LeaveApprovalStatus; requested_hours: number; payable_hours: number | null }>; +// const statuses = Object.values(LeaveApprovalStatus); +// const created = [] as Array<{ id: number; employee_id: number; leave_type: LeaveTypes; date: Date; comment: string; approval_status: LeaveApprovalStatus; requested_hours: number; payable_hours: number | null }>; - const COUNT = 12; - for (let i = 0; i < COUNT; i++) { - const emp = employees[i % employees.length]; - const leaveCode = leaveCodes[Math.floor(Math.random() * leaveCodes.length)]; +// const COUNT = 12; +// for (let i = 0; i < COUNT; i++) { +// const emp = employees[i % employees.length]; +// const leaveCode = leaveCodes[Math.floor(Math.random() * leaveCodes.length)]; - const date = daysAgo(120 - i * 3); - const status = statuses[(i + 2) % statuses.length]; - const requestedHours = 4 + (i % 5); // 4 ? 8 h - const payableHours = status === LeaveApprovalStatus.APPROVED ? Math.min(requestedHours, 8) : null; +// const date = daysAgo(120 - i * 3); +// const status = statuses[(i + 2) % statuses.length]; +// const requestedHours = 4 + (i % 5); // 4 ? 8 h +// const payableHours = status === LeaveApprovalStatus.APPROVED ? Math.min(requestedHours, 8) : null; - const lr = await prisma.leaveRequests.create({ - data: { - employee_id: emp.id, - bank_code_id: leaveCode.id, - leave_type: leaveCode.type as LeaveTypes, - date, - comment: `Past leave #${i + 1} (${leaveCode.type})`, - approval_status: status, - requested_hours: requestedHours, - payable_hours: payableHours, - }, - }); +// const lr = await prisma.leaveRequests.create({ +// data: { +// employee_id: emp.id, +// bank_code_id: leaveCode.id, +// leave_type: leaveCode.type as LeaveTypes, +// date, +// comment: `Past leave #${i + 1} (${leaveCode.type})`, +// approval_status: status, +// requested_hours: requestedHours, +// payable_hours: payableHours, +// }, +// }); - created.push({ - id: lr.id, - employee_id: lr.employee_id, - leave_type: lr.leave_type, - date: lr.date, - comment: lr.comment, - approval_status: lr.approval_status, - requested_hours: requestedHours, - payable_hours: payableHours, - }); - } +// created.push({ +// id: lr.id, +// employee_id: lr.employee_id, +// leave_type: lr.leave_type, +// date: lr.date, +// comment: lr.comment, +// approval_status: lr.approval_status, +// requested_hours: requestedHours, +// payable_hours: payableHours, +// }); +// } - for (const lr of created) { - await prisma.leaveRequestsArchive.create({ - data: { - leave_request_id: lr.id, - employee_id: lr.employee_id, - leave_type: lr.leave_type, - date: lr.date, - comment: lr.comment, - approval_status: lr.approval_status, - requested_hours: lr.requested_hours, - payable_hours: lr.payable_hours, - }, - }); - } +// for (const lr of created) { +// await prisma.leaveRequestsArchive.create({ +// data: { +// leave_request_id: lr.id, +// employee_id: lr.employee_id, +// leave_type: lr.leave_type, +// date: lr.date, +// comment: lr.comment, +// approval_status: lr.approval_status, +// requested_hours: lr.requested_hours, +// payable_hours: lr.payable_hours, +// }, +// }); +// } - console.log(`? LeaveRequestsArchive: ${created.length} rows`); -} +// console.log(`? LeaveRequestsArchive: ${created.length} rows`); +// } -main().finally(() => prisma.$disconnect()); \ No newline at end of file +// main().finally(() => prisma.$disconnect()); \ No newline at end of file diff --git a/prisma/mock-seeds-scripts/11-shifts-archive.ts b/prisma/mock-seeds-scripts/11-shifts-archive.ts index 1c3f5e8..4259359 100644 --- a/prisma/mock-seeds-scripts/11-shifts-archive.ts +++ b/prisma/mock-seeds-scripts/11-shifts-archive.ts @@ -1,71 +1,71 @@ -import { PrismaClient } from '@prisma/client'; +// import { PrismaClient } from '@prisma/client'; -if (process.env.SKIP_LEAVE_REQUESTS === 'true') { - console.log("â­ Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)"); - process.exit(0); -} +// if (process.env.SKIP_LEAVE_REQUESTS === 'true') { +// console.log("â­ Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)"); +// process.exit(0); +// } -const prisma = new PrismaClient(); +// const prisma = new PrismaClient(); -function timeAt(h:number,m:number) { - return new Date(Date.UTC(1970,0,1,h,m,0)); -} -function daysAgo(n:number) { - const d = new Date(); - d.setUTCDate(d.getUTCDate() - n); - d.setUTCHours(0,0,0,0); - return d; -} +// function timeAt(h:number,m:number) { +// return new Date(Date.UTC(1970,0,1,h,m,0)); +// } +// function daysAgo(n:number) { +// const d = new Date(); +// d.setUTCDate(d.getUTCDate() - n); +// d.setUTCHours(0,0,0,0); +// return d; +// } -async function main() { - const bankCodes = await prisma.bankCodes.findMany({ where: { categorie: 'SHIFT' }, select: { id: true } }); - const employees = await prisma.employees.findMany({ select: { id: true } }); +// async function main() { +// const bankCodes = await prisma.bankCodes.findMany({ where: { categorie: 'SHIFT' }, select: { id: true } }); +// const employees = await prisma.employees.findMany({ select: { id: true } }); - for (const e of employees) { - const tss = await prisma.timesheets.findMany({ where: { employee_id: e.id }, select: { id: true } }); - if (!tss.length) continue; +// for (const e of employees) { +// const tss = await prisma.timesheets.findMany({ where: { employee_id: e.id }, select: { id: true } }); +// if (!tss.length) continue; - const createdShiftIds: number[] = []; - for (let i = 0; i < 8; i++) { - const ts = tss[i % tss.length]; - const bc = bankCodes[i % bankCodes.length]; - const date = daysAgo(200 + i); // bien dans le passé - const startH = 7 + (i % 4); // 7..10 - const endH = startH + 8; // 15..18 +// const createdShiftIds: number[] = []; +// for (let i = 0; i < 8; i++) { +// const ts = tss[i % tss.length]; +// const bc = bankCodes[i % bankCodes.length]; +// const date = daysAgo(200 + i); // bien dans le passé +// const startH = 7 + (i % 4); // 7..10 +// const endH = startH + 8; // 15..18 - const sh = await prisma.shifts.create({ - data: { - timesheet_id: ts.id, - bank_code_id: bc.id, - comment: `Archived-era shift ${i + 1} for emp ${e.id}`, - date, - start_time: timeAt(startH, 0), - end_time: timeAt(endH, 0), - is_approved: true, - }, - }); - createdShiftIds.push(sh.id); - } +// const sh = await prisma.shifts.create({ +// data: { +// timesheet_id: ts.id, +// bank_code_id: bc.id, +// comment: `Archived-era shift ${i + 1} for emp ${e.id}`, +// date, +// start_time: timeAt(startH, 0), +// end_time: timeAt(endH, 0), +// is_approved: true, +// }, +// }); +// createdShiftIds.push(sh.id); +// } - for (const sid of createdShiftIds) { - const s = await prisma.shifts.findUnique({ where: { id: sid } }); - if (!s) continue; - await prisma.shiftsArchive.create({ - data: { - shift_id: s.id, - timesheet_id: s.timesheet_id, - bank_code_id: s.bank_code_id, - comment: s.comment, - date: s.date, - start_time: s.start_time, - end_time: s.end_time, - }, - }); - } - } +// for (const sid of createdShiftIds) { +// const s = await prisma.shifts.findUnique({ where: { id: sid } }); +// if (!s) continue; +// await prisma.shiftsArchive.create({ +// data: { +// shift_id: s.id, +// timesheet_id: s.timesheet_id, +// bank_code_id: s.bank_code_id, +// comment: s.comment, +// date: s.date, +// start_time: s.start_time, +// end_time: s.end_time, +// }, +// }); +// } +// } - const total = await prisma.shiftsArchive.count(); - console.log(`✓ ShiftsArchive: ${total} rows total`); -} +// const total = await prisma.shiftsArchive.count(); +// console.log(`✓ ShiftsArchive: ${total} rows total`); +// } -main().finally(() => prisma.$disconnect()); +// main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/13-expenses-archive.ts b/prisma/mock-seeds-scripts/13-expenses-archive.ts index d8e35a2..c56520e 100644 --- a/prisma/mock-seeds-scripts/13-expenses-archive.ts +++ b/prisma/mock-seeds-scripts/13-expenses-archive.ts @@ -1,65 +1,65 @@ -// 13-expenses-archive.ts -import { PrismaClient, Expenses } from '@prisma/client'; +// // 13-expenses-archive.ts +// import { PrismaClient, Expenses } from '@prisma/client'; -if (process.env.SKIP_LEAVE_REQUESTS === 'true') { - console.log("â­ Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)"); - process.exit(0); -} +// if (process.env.SKIP_LEAVE_REQUESTS === 'true') { +// console.log("â­ Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)"); +// process.exit(0); +// } -const prisma = new PrismaClient(); +// const prisma = new PrismaClient(); -function daysAgo(n:number) { - const d = new Date(); - d.setUTCDate(d.getUTCDate() - n); - d.setUTCHours(0,0,0,0); - return d; -} +// function daysAgo(n:number) { +// const d = new Date(); +// d.setUTCDate(d.getUTCDate() - n); +// d.setUTCHours(0,0,0,0); +// return d; +// } -async function main() { - const expenseCodes = await prisma.bankCodes.findMany({ where: { categorie: 'EXPENSE' }, select: { id: true } }); - const timesheets = await prisma.timesheets.findMany({ select: { id: true } }); +// async function main() { +// const expenseCodes = await prisma.bankCodes.findMany({ where: { categorie: 'EXPENSE' }, select: { id: true } }); +// const timesheets = await prisma.timesheets.findMany({ select: { id: true } }); - // ✅ typer pour éviter never[] - const created: Expenses[] = []; +// // ✅ typer pour éviter never[] +// const created: Expenses[] = []; - for (let i = 0; i < 4; i++) { - const ts = timesheets[i % timesheets.length]; - const bc = expenseCodes[i % expenseCodes.length]; +// for (let i = 0; i < 4; i++) { +// const ts = timesheets[i % timesheets.length]; +// const bc = expenseCodes[i % expenseCodes.length]; - const exp = await prisma.expenses.create({ - data: { - timesheet_id: ts.id, - bank_code_id: bc.id, - date: daysAgo(60 + i), - amount: (20 + i * 3.5).toFixed(2), // ok: Decimal accepte string - attachment: null, - comment: `Old expense #${i + 1}`, - is_approved: true, - supervisor_comment: null, - }, - }); +// const exp = await prisma.expenses.create({ +// data: { +// timesheet_id: ts.id, +// bank_code_id: bc.id, +// date: daysAgo(60 + i), +// amount: (20 + i * 3.5).toFixed(2), // ok: Decimal accepte string +// attachment: null, +// comment: `Old expense #${i + 1}`, +// is_approved: true, +// supervisor_comment: null, +// }, +// }); - created.push(exp); - } +// created.push(exp); +// } - for (const e of created) { - await prisma.expensesArchive.create({ - data: { - expense_id: e.id, - timesheet_id: e.timesheet_id, - bank_code_id: e.bank_code_id, - date: e.date, - amount: e.amount, - attachment: e.attachment, - comment: e.comment, - is_approved: e.is_approved, - supervisor_comment: e.supervisor_comment, - }, - }); - } +// for (const e of created) { +// await prisma.expensesArchive.create({ +// data: { +// expense_id: e.id, +// timesheet_id: e.timesheet_id, +// bank_code_id: e.bank_code_id, +// date: e.date, +// amount: e.amount, +// attachment: e.attachment, +// comment: e.comment, +// is_approved: e.is_approved, +// supervisor_comment: e.supervisor_comment, +// }, +// }); +// } - const total = await prisma.expensesArchive.count(); - console.log(`✓ ExpensesArchive: ${total} total rows (added ${created.length})`); -} +// const total = await prisma.expensesArchive.count(); +// console.log(`✓ ExpensesArchive: ${total} total rows (added ${created.length})`); +// } -main().finally(() => prisma.$disconnect()); +// main().finally(() => prisma.$disconnect()); diff --git a/src/app.module.ts b/src/app.module.ts index 60c2767..901f74e 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -4,58 +4,45 @@ import { AppService } from './app.service'; // import { ArchivalModule } from './modules/archival/archival.module'; import { AuthenticationModule } from './modules/authentication/auth.module'; import { BankCodesModule } from './modules/bank-codes/bank-codes.module'; -import { BusinessLogicsModule } from './modules/business-logics/business-logics.module'; // import { CsvExportModule } from './modules/exports/csv-exports.module'; -import { CustomersModule } from './modules/customers/customers.module'; -import { EmployeesModule } from './modules/employees/employees.module'; -// import { ExpensesModule } from './modules/expenses/expenses.module'; import { HealthModule } from './health/health.module'; import { HealthController } from './health/health.controller'; -// import { LeaveRequestsModule } from './modules/leave-requests/leave-requests.module'; import { NotificationsModule } from './modules/notifications/notifications.module'; import { OauthSessionsModule } from './modules/oauth-sessions/oauth-sessions.module'; -import { OvertimeService } from './modules/business-logics/services/overtime.service'; import { PreferencesModule } from './modules/preferences/preferences.module'; import { PrismaModule } from './prisma/prisma.module'; import { ScheduleModule } from '@nestjs/schedule'; -import { ShiftsModule } from './Time_And_Attendance/modules/time-tracker/shifts/shifts.module'; -import { TimesheetsModule } from './Time_And_Attendance/modules/time-tracker/timesheets/timesheets.module'; import { UsersModule } from './modules/users-management/users.module'; import { ConfigModule } from '@nestjs/config'; import { APP_FILTER, APP_PIPE } from '@nestjs/core'; import { HttpExceptionFilter } from './common/filters/http-exception.filter'; import { ValidationError } from 'class-validator'; -import { SchedulePresetsModule } from './modules/schedule-presets/schedule-presets.module'; -import { PayperiodsModule } from './Time_And_Attendance/modules/pay-period/pay-periods.module'; +import { PayperiodsModule } from './time-and-attendance/modules/pay-period/pay-periods.module'; +import { TimeAndAttendanceModule } from 'src/time-and-attendance/time-and-attendance.module'; @Module({ imports: [ - // ArchivalModule, AuthenticationModule, BankCodesModule, - BusinessLogicsModule, ConfigModule.forRoot({isGlobal: true}), // CsvExportModule, - CustomersModule, - EmployeesModule, - // ExpensesModule, + // CustomersModule, + // EmployeesModule, HealthModule, - // LeaveRequestsModule, NotificationsModule, OauthSessionsModule, PayperiodsModule, PreferencesModule, PrismaModule, ScheduleModule.forRoot(), //cronjobs - SchedulePresetsModule, - ShiftsModule, - TimesheetsModule, + // ShiftsModule, + TimeAndAttendanceModule, + // TimesheetsModule, UsersModule, ], controllers: [AppController, HealthController], providers: [ AppService, - OvertimeService, { provide: APP_FILTER, useClass: HttpExceptionFilter diff --git a/src/identity-and-account/employees/employees.module.ts b/src/identity-and-account/employees/employees.module.ts new file mode 100644 index 0000000..7ce6f04 --- /dev/null +++ b/src/identity-and-account/employees/employees.module.ts @@ -0,0 +1,13 @@ +// import { Module } from '@nestjs/common'; +// import { EmployeesController } from './controllers/employees.controller'; +// import { EmployeesService } from './services/employees.service'; +// import { EmployeesArchivalService } from './services/employees-archival.service'; +// import { SharedModule } from '../../time-and-attendance/modules/shared/shared.module'; + +// @Module({ +// imports: [SharedModule], +// controllers: [EmployeesController], +// providers: [EmployeesService, EmployeesArchivalService], +// exports: [EmployeesService, EmployeesArchivalService], +// }) +// export class EmployeesModule {} diff --git a/src/modules/archival/controllers/employees-archive.controller.ts b/src/modules/archival/controllers/employees-archive.controller.ts index 375a351..776c6fa 100644 --- a/src/modules/archival/controllers/employees-archive.controller.ts +++ b/src/modules/archival/controllers/employees-archive.controller.ts @@ -1,33 +1,33 @@ -import { Controller, Get, NotFoundException, Param, ParseIntPipe, UseGuards } from "@nestjs/common"; -import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { EmployeesArchive, Roles as RoleEnum } from '@prisma/client'; -import { EmployeesArchivalService } from "src/modules/employees/services/employees-archival.service"; +// import { Controller, Get, NotFoundException, Param, ParseIntPipe, UseGuards } from "@nestjs/common"; +// import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +// import { RolesAllowed } from "src/common/decorators/roles.decorators"; +// import { EmployeesArchive, Roles as RoleEnum } from '@prisma/client'; +// import { EmployeesArchivalService } from "src/modules/employees/services/employees-archival.service"; -@ApiTags('Employee Archives') -// @UseGuards() -@Controller('archives/employees') -export class EmployeesArchiveController { - constructor(private readonly employeesArchiveService: EmployeesArchivalService) {} +// @ApiTags('Employee Archives') +// // @UseGuards() +// @Controller('archives/employees') +// export class EmployeesArchiveController { +// constructor(private readonly employeesArchiveService: EmployeesArchivalService) {} - @Get() - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) - @ApiOperation({ summary: 'List of archived employees'}) - @ApiResponse({ status: 200, description: 'List of archived employees', isArray: true }) - async findAllArchived(): Promise { - return this.employeesArchiveService.findAllArchived(); - } +// @Get() +// //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) +// @ApiOperation({ summary: 'List of archived employees'}) +// @ApiResponse({ status: 200, description: 'List of archived employees', isArray: true }) +// async findAllArchived(): Promise { +// return this.employeesArchiveService.findAllArchived(); +// } - @Get() - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR,RoleEnum.SUPERVISOR) - @ApiOperation({ summary: 'Fetch employee in archives with its Id'}) - @ApiResponse({ status: 200, description: 'Archived employee found'}) - async findOneArchived(@Param('id', ParseIntPipe) id: number ): Promise { - try{ - return await this.employeesArchiveService.findOneArchived(id); - }catch { - throw new NotFoundException(`Archived employee #${id} not found`); - } - } +// @Get() +// //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR,RoleEnum.SUPERVISOR) +// @ApiOperation({ summary: 'Fetch employee in archives with its Id'}) +// @ApiResponse({ status: 200, description: 'Archived employee found'}) +// async findOneArchived(@Param('id', ParseIntPipe) id: number ): Promise { +// try{ +// return await this.employeesArchiveService.findOneArchived(id); +// }catch { +// throw new NotFoundException(`Archived employee #${id} not found`); +// } +// } -} \ No newline at end of file +// } \ No newline at end of file diff --git a/src/modules/archival/controllers/expenses-archive.controller.ts b/src/modules/archival/controllers/expenses-archive.controller.ts index e6bd935..f256b82 100644 --- a/src/modules/archival/controllers/expenses-archive.controller.ts +++ b/src/modules/archival/controllers/expenses-archive.controller.ts @@ -2,7 +2,7 @@ import { UseGuards, Controller, Get, Param, ParseIntPipe, NotFoundException } fr import { ApiTags, ApiOperation, ApiResponse } from "@nestjs/swagger"; import { ExpensesArchive,Roles as RoleEnum } from "@prisma/client"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { ExpensesArchivalService } from "src/modules/expenses/services/expenses-archival.service"; +import { ExpensesArchivalService } from "src/time-and-attendance/modules/expenses/services/expenses-archival.service"; @ApiTags('Expense Archives') // @UseGuards() diff --git a/src/modules/archival/controllers/shifts-archive.controller.ts b/src/modules/archival/controllers/shifts-archive.controller.ts index 463ea4a..7c88288 100644 --- a/src/modules/archival/controllers/shifts-archive.controller.ts +++ b/src/modules/archival/controllers/shifts-archive.controller.ts @@ -2,7 +2,7 @@ import { Get, Param, ParseIntPipe, NotFoundException, Controller, UseGuards } fr import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { ShiftsArchive, Roles as RoleEnum } from "@prisma/client"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { ShiftsArchivalService } from "src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-archival.service"; +import { ShiftsArchivalService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service"; @ApiTags('Shift Archives') // @UseGuards() diff --git a/src/modules/archival/controllers/timesheets-archive.controller.ts b/src/modules/archival/controllers/timesheets-archive.controller.ts index 9be0116..3a8f0a2 100644 --- a/src/modules/archival/controllers/timesheets-archive.controller.ts +++ b/src/modules/archival/controllers/timesheets-archive.controller.ts @@ -2,7 +2,7 @@ import { Controller, Get, NotFoundException, Param, ParseIntPipe, UseGuards } fr import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { TimesheetsArchive, Roles as RoleEnum } from '@prisma/client'; -import { TimesheetArchiveService } from "src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-archive.service"; +import { TimesheetArchiveService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service"; @ApiTags('Timesheet Archives') // @UseGuards() diff --git a/src/modules/archival/services/archival.service.ts b/src/modules/archival/services/archival.service.ts index 898ab51..ded45eb 100644 --- a/src/modules/archival/services/archival.service.ts +++ b/src/modules/archival/services/archival.service.ts @@ -1,8 +1,8 @@ import { Injectable, Logger } from "@nestjs/common"; import { Cron } from "@nestjs/schedule"; -import { ExpensesArchivalService } from "src/modules/expenses/services/expenses-archival.service"; -import { ShiftsArchivalService } from "src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-archival.service"; -import { TimesheetArchiveService } from "src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-archive.service"; +import { ExpensesArchivalService } from "src/time-and-attendance/modules/expenses/services/expenses-archival.service"; +import { ShiftsArchivalService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service"; +import { TimesheetArchiveService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service"; @Injectable() export class ArchivalService { diff --git a/src/modules/customers/controllers/customers.controller.ts b/src/modules/customers/controllers/customers.controller.ts index 713ebde..c066342 100644 --- a/src/modules/customers/controllers/customers.controller.ts +++ b/src/modules/customers/controllers/customers.controller.ts @@ -1,68 +1,68 @@ -import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from '@nestjs/common'; -import { CustomersService } from '../services/customers.service'; -import { Customers } from '@prisma/client'; -import { CreateCustomerDto } from '../dtos/create-customer.dto'; -import { UpdateCustomerDto } from '../dtos/update-customer.dto'; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { Roles as RoleEnum } from '.prisma/client'; -import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +// import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from '@nestjs/common'; +// import { CustomersService } from '../services/customers.service'; +// import { Customers } from '@prisma/client'; +// import { CreateCustomerDto } from '../dtos/create-customer.dto'; +// import { UpdateCustomerDto } from '../dtos/update-customer.dto'; +// import { RolesAllowed } from "src/common/decorators/roles.decorators"; +// import { Roles as RoleEnum } from '.prisma/client'; +// import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; -@ApiTags('Customers') -@ApiBearerAuth('access-token') -// @UseGuards() -@Controller('customers') -export class CustomersController { - constructor(private readonly customersService: CustomersService) {} +// @ApiTags('Customers') +// @ApiBearerAuth('access-token') +// // @UseGuards() +// @Controller('customers') +// export class CustomersController { +// constructor(private readonly customersService: CustomersService) {} -//_____________________________________________________________________________________________ -// Deprecated or unused methods -//_____________________________________________________________________________________________ +// //_____________________________________________________________________________________________ +// // Deprecated or unused methods +// //_____________________________________________________________________________________________ - // @Post() - // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.SUPERVISOR) - // @ApiOperation({ summary: 'Create customer' }) - // @ApiResponse({ status: 201, description: 'Customer created', type: CreateCustomerDto }) - // @ApiResponse({ status: 400, description: 'Invalid task or invalid data' }) - // create(@Body() dto: CreateCustomerDto): Promise { - // return this.customersService.create(dto); - // } +// // @Post() +// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.SUPERVISOR) +// // @ApiOperation({ summary: 'Create customer' }) +// // @ApiResponse({ status: 201, description: 'Customer created', type: CreateCustomerDto }) +// // @ApiResponse({ status: 400, description: 'Invalid task or invalid data' }) +// // create(@Body() dto: CreateCustomerDto): Promise { +// // return this.customersService.create(dto); +// // } - // @Get() - // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) - // @ApiOperation({ summary: 'Find all customers' }) - // @ApiResponse({ status: 201, description: 'List of customers found', type: CreateCustomerDto, isArray: true }) - // @ApiResponse({ status: 400, description: 'List of customers not found' }) - // findAll(): Promise { - // return this.customersService.findAll(); - // } +// // @Get() +// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) +// // @ApiOperation({ summary: 'Find all customers' }) +// // @ApiResponse({ status: 201, description: 'List of customers found', type: CreateCustomerDto, isArray: true }) +// // @ApiResponse({ status: 400, description: 'List of customers not found' }) +// // findAll(): Promise { +// // return this.customersService.findAll(); +// // } - // @Get(':id') - // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) - // @ApiOperation({ summary: 'Find customer' }) - // @ApiResponse({ status: 201, description: 'Customer found', type: CreateCustomerDto }) - // @ApiResponse({ status: 400, description: 'Customer not found' }) - // findOne(@Param('id', ParseIntPipe) id: number): Promise { - // return this.customersService.findOne(id); - // } +// // @Get(':id') +// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) +// // @ApiOperation({ summary: 'Find customer' }) +// // @ApiResponse({ status: 201, description: 'Customer found', type: CreateCustomerDto }) +// // @ApiResponse({ status: 400, description: 'Customer not found' }) +// // findOne(@Param('id', ParseIntPipe) id: number): Promise { +// // return this.customersService.findOne(id); +// // } - // @Patch(':id') - // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE,RoleEnum.SUPERVISOR) - // @ApiOperation({ summary: 'Update customer' }) - // @ApiResponse({ status: 201, description: 'Customer updated', type: CreateCustomerDto }) - // @ApiResponse({ status: 400, description: 'Customer not found' }) - // update( - // @Param('id', ParseIntPipe) id: number, - // @Body() dto: UpdateCustomerDto, - // ): Promise { - // return this.customersService.update(id, dto); - // } +// // @Patch(':id') +// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE,RoleEnum.SUPERVISOR) +// // @ApiOperation({ summary: 'Update customer' }) +// // @ApiResponse({ status: 201, description: 'Customer updated', type: CreateCustomerDto }) +// // @ApiResponse({ status: 400, description: 'Customer not found' }) +// // update( +// // @Param('id', ParseIntPipe) id: number, +// // @Body() dto: UpdateCustomerDto, +// // ): Promise { +// // return this.customersService.update(id, dto); +// // } - // @Delete(':id') - // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.SUPERVISOR) - // @ApiOperation({ summary: 'Delete customer' }) - // @ApiResponse({ status: 201, description: 'Customer deleted', type: CreateCustomerDto }) - // @ApiResponse({ status: 400, description: 'Customer not found' }) - // remove(@Param('id', ParseIntPipe) id: number): Promise{ - // return this.customersService.remove(id); - // } -} +// // @Delete(':id') +// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.SUPERVISOR) +// // @ApiOperation({ summary: 'Delete customer' }) +// // @ApiResponse({ status: 201, description: 'Customer deleted', type: CreateCustomerDto }) +// // @ApiResponse({ status: 400, description: 'Customer not found' }) +// // remove(@Param('id', ParseIntPipe) id: number): Promise{ +// // return this.customersService.remove(id); +// // } +// } diff --git a/src/modules/customers/customers.module.ts b/src/modules/customers/customers.module.ts index c9e3f9d..cccb363 100644 --- a/src/modules/customers/customers.module.ts +++ b/src/modules/customers/customers.module.ts @@ -1,10 +1,10 @@ -import { Module } from '@nestjs/common'; -import { CustomersController } from './controllers/customers.controller'; -import { CustomersService } from './services/customers.service'; +// import { Module } from '@nestjs/common'; +// import { CustomersController } from './controllers/customers.controller'; +// import { CustomersService } from './services/customers.service'; -@Module({ - controllers:[CustomersController], - providers:[CustomersService], -}) -export class CustomersModule {} \ No newline at end of file +// @Module({ +// controllers:[CustomersController], +// providers:[CustomersService], +// }) +// export class CustomersModule {} \ No newline at end of file diff --git a/src/modules/employees/controllers/employees.controller.ts b/src/modules/employees/controllers/employees.controller.ts index e647547..4828d91 100644 --- a/src/modules/employees/controllers/employees.controller.ts +++ b/src/modules/employees/controllers/employees.controller.ts @@ -1,99 +1,99 @@ -import { Body,Controller,Get,NotFoundException,Param,Patch } from '@nestjs/common'; -import { EmployeesService } from '../services/employees.service'; -import { CreateEmployeeDto } from '../dtos/create-employee.dto'; -import { UpdateEmployeeDto } from '../dtos/update-employee.dto'; -import { RolesAllowed } from '../../../common/decorators/roles.decorators'; -import { ApiBearerAuth, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger'; -import { EmployeeListItemDto } from '../dtos/list-employee.dto'; -import { EmployeesArchivalService } from '../services/employees-archival.service'; -import { EmployeeProfileItemDto } from 'src/modules/employees/dtos/profil-employee.dto'; +// import { Body,Controller,Get,NotFoundException,Param,Patch } from '@nestjs/common'; +// import { EmployeesService } from '../services/employees.service'; +// import { CreateEmployeeDto } from '../dtos/create-employee.dto'; +// import { UpdateEmployeeDto } from '../dtos/update-employee.dto'; +// import { RolesAllowed } from '../../../common/decorators/roles.decorators'; +// import { ApiBearerAuth, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger'; +// import { EmployeeListItemDto } from '../dtos/list-employee.dto'; +// import { EmployeesArchivalService } from '../services/employees-archival.service'; +// import { EmployeeProfileItemDto } from 'src/modules/employees/dtos/profil-employee.dto'; -@ApiTags('Employees') -@ApiBearerAuth('access-token') -// @UseGuards() -@Controller('employees') -export class EmployeesController { - constructor( - private readonly employeesService: EmployeesService, - private readonly archiveService: EmployeesArchivalService, - ) {} +// @ApiTags('Employees') +// @ApiBearerAuth('access-token') +// // @UseGuards() +// @Controller('employees') +// export class EmployeesController { +// constructor( +// private readonly employeesService: EmployeesService, +// private readonly archiveService: EmployeesArchivalService, +// ) {} - @Get('employee-list') - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ACCOUNTING) - @ApiOperation({summary: 'Find all employees with scoped info' }) - @ApiResponse({ status: 200, description: 'List of employees with scoped info found', type: EmployeeListItemDto, isArray: true }) - @ApiResponse({ status: 400, description: 'List of employees with scoped info not found' }) - findListEmployees(): Promise { - return this.employeesService.findListEmployees(); - } +// @Get('employee-list') +// //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ACCOUNTING) +// @ApiOperation({summary: 'Find all employees with scoped info' }) +// @ApiResponse({ status: 200, description: 'List of employees with scoped info found', type: EmployeeListItemDto, isArray: true }) +// @ApiResponse({ status: 400, description: 'List of employees with scoped info not found' }) +// findListEmployees(): Promise { +// return this.employeesService.findListEmployees(); +// } - @Patch(':email') - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) - @ApiBearerAuth('access-token') - @ApiOperation({ summary: 'Update, archive or restore an employee' }) - @ApiParam({ name: 'email', type: Number, description: 'Email of the employee' }) - @ApiResponse({ status: 200, description: 'Employee updated or restored', type: CreateEmployeeDto }) - @ApiResponse({ status: 202, description: 'Employee archived successfully', type: CreateEmployeeDto }) - @ApiResponse({ status: 404, description: 'Employee not found in active or archive' }) - async updateOrArchiveOrRestore(@Param('email') email: string, @Body() dto: UpdateEmployeeDto,) { - // if last_work_day is set => archive the employee - // else if employee is archived and first_work_day or last_work_day = null => restore - //otherwise => standard update - const result = await this.archiveService.patchEmployee(email, dto); - if(!result) { - throw new NotFoundException(`Employee with email: ${ email } is not found in active or archive.`) - } - return result; - } +// @Patch(':email') +// //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) +// @ApiBearerAuth('access-token') +// @ApiOperation({ summary: 'Update, archive or restore an employee' }) +// @ApiParam({ name: 'email', type: Number, description: 'Email of the employee' }) +// @ApiResponse({ status: 200, description: 'Employee updated or restored', type: CreateEmployeeDto }) +// @ApiResponse({ status: 202, description: 'Employee archived successfully', type: CreateEmployeeDto }) +// @ApiResponse({ status: 404, description: 'Employee not found in active or archive' }) +// async updateOrArchiveOrRestore(@Param('email') email: string, @Body() dto: UpdateEmployeeDto,) { +// // if last_work_day is set => archive the employee +// // else if employee is archived and first_work_day or last_work_day = null => restore +// //otherwise => standard update +// const result = await this.archiveService.patchEmployee(email, dto); +// if(!result) { +// throw new NotFoundException(`Employee with email: ${ email } is not found in active or archive.`) +// } +// return result; +// } - //_____________________________________________________________________________________________ - // Deprecated or unused methods - //_____________________________________________________________________________________________ +// //_____________________________________________________________________________________________ +// // Deprecated or unused methods +// //_____________________________________________________________________________________________ - // @Post() - // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) - // @ApiOperation({summary: 'Create employee' }) - // @ApiResponse({ status: 201, description: 'Employee created', type: CreateEmployeeDto }) - // @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' }) - // create(@Body() dto: CreateEmployeeDto): Promise { - // return this.employeesService.create(dto); - // } - // @Get() - // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ACCOUNTING) - // @ApiOperation({summary: 'Find all employees' }) - // @ApiResponse({ status: 200, description: 'List of employees found', type: CreateEmployeeDto, isArray: true }) - // @ApiResponse({ status: 400, description: 'List of employees not found' }) - // findAll(): Promise { - // return this.employeesService.findAll(); - // } +// // @Post() +// // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) +// // @ApiOperation({summary: 'Create employee' }) +// // @ApiResponse({ status: 201, description: 'Employee created', type: CreateEmployeeDto }) +// // @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' }) +// // create(@Body() dto: CreateEmployeeDto): Promise { +// // return this.employeesService.create(dto); +// // } +// // @Get() +// // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ACCOUNTING) +// // @ApiOperation({summary: 'Find all employees' }) +// // @ApiResponse({ status: 200, description: 'List of employees found', type: CreateEmployeeDto, isArray: true }) +// // @ApiResponse({ status: 400, description: 'List of employees not found' }) +// // findAll(): Promise { +// // return this.employeesService.findAll(); +// // } - // @Get(':email') - // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR,RoleEnum.ACCOUNTING ) - // @ApiOperation({summary: 'Find employee' }) - // @ApiResponse({ status: 200, description: 'Employee found', type: CreateEmployeeDto }) - // @ApiResponse({ status: 400, description: 'Employee not found' }) - // findOne(@Param('email', ParseIntPipe) email: string): Promise { - // return this.employeesService.findOne(email); - // } +// // @Get(':email') +// // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR,RoleEnum.ACCOUNTING ) +// // @ApiOperation({summary: 'Find employee' }) +// // @ApiResponse({ status: 200, description: 'Employee found', type: CreateEmployeeDto }) +// // @ApiResponse({ status: 400, description: 'Employee not found' }) +// // findOne(@Param('email', ParseIntPipe) email: string): Promise { +// // return this.employeesService.findOne(email); +// // } - @Get('profile/:email') - @ApiOperation({summary: 'Find employee profile' }) - @ApiParam({ name: 'email', type: String, description: 'Identifier of the employee' }) - @ApiResponse({ status: 200, description: 'Employee profile found', type: EmployeeProfileItemDto }) - @ApiResponse({ status: 400, description: 'Employee profile not found' }) - findOneProfile(@Param('email') email: string): Promise { - return this.employeesService.findOneProfile(email); - } +// @Get('profile/:email') +// @ApiOperation({summary: 'Find employee profile' }) +// @ApiParam({ name: 'email', type: String, description: 'Identifier of the employee' }) +// @ApiResponse({ status: 200, description: 'Employee profile found', type: EmployeeProfileItemDto }) +// @ApiResponse({ status: 400, description: 'Employee profile not found' }) +// findOneProfile(@Param('email') email: string): Promise { +// return this.employeesService.findOneProfile(email); +// } - // @Delete(':email') - // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR ) - // @ApiOperation({summary: 'Delete employee' }) - // @ApiParam({ name: 'email', type: Number, description: 'Email of the employee to delete' }) - // @ApiResponse({ status: 204, description: 'Employee deleted' }) - // @ApiResponse({ status: 404, description: 'Employee not found' }) - // remove(@Param('email', ParseIntPipe) email: string): Promise { - // return this.employeesService.remove(email); - // } +// // @Delete(':email') +// // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR ) +// // @ApiOperation({summary: 'Delete employee' }) +// // @ApiParam({ name: 'email', type: Number, description: 'Email of the employee to delete' }) +// // @ApiResponse({ status: 204, description: 'Employee deleted' }) +// // @ApiResponse({ status: 404, description: 'Employee not found' }) +// // remove(@Param('email', ParseIntPipe) email: string): Promise { +// // return this.employeesService.remove(email); +// // } -} +// } diff --git a/src/modules/employees/employees.module.ts b/src/modules/employees/employees.module.ts index 2fd40ee..676f4b0 100644 --- a/src/modules/employees/employees.module.ts +++ b/src/modules/employees/employees.module.ts @@ -1,13 +1,12 @@ -import { Module } from '@nestjs/common'; -import { EmployeesController } from './controllers/employees.controller'; -import { EmployeesService } from './services/employees.service'; -import { EmployeesArchivalService } from './services/employees-archival.service'; -import { SharedModule } from '../../Time_And_Attendance/modules/shared/shared.module'; +// import { Module } from '@nestjs/common'; +// import { EmployeesController } from './controllers/employees.controller'; +// import { EmployeesService } from './services/employees.service'; +// import { SharedModule } from '../../time-and-attendance/modules/shared/shared.module'; -@Module({ - imports: [SharedModule], - controllers: [EmployeesController], - providers: [EmployeesService, EmployeesArchivalService], - exports: [EmployeesService, EmployeesArchivalService], -}) -export class EmployeesModule {} +// @Module({ +// imports: [SharedModule], +// controllers: [EmployeesController], +// providers: [EmployeesService], +// exports: [EmployeesService ], +// }) +// export class EmployeesModule {} diff --git a/src/modules/employees/services/employees-archival.service.ts b/src/modules/employees/services/employees-archival.service.ts index b13fa74..6046e94 100644 --- a/src/modules/employees/services/employees-archival.service.ts +++ b/src/modules/employees/services/employees-archival.service.ts @@ -1,173 +1,173 @@ -import { Injectable } from "@nestjs/common"; -import { Employees, EmployeesArchive, Users } from "@prisma/client"; -import { PrismaService } from "src/prisma/prisma.service"; -import { UpdateEmployeeDto } from "../dtos/update-employee.dto"; -import { toDateOrUndefined, toDateOrNull } from "../utils/employee.utils"; +// import { Injectable } from "@nestjs/common"; +// import { Employees, EmployeesArchive, Users } from "@prisma/client"; +// import { PrismaService } from "src/prisma/prisma.service"; +// import { UpdateEmployeeDto } from "../dtos/update-employee.dto"; +// import { toDateOrUndefined, toDateOrNull } from "../utils/employee.utils"; -@Injectable() -export class EmployeesArchivalService { - constructor(private readonly prisma: PrismaService) { } +// @Injectable() +// export class EmployeesArchivalService { +// constructor(private readonly prisma: PrismaService) { } - async patchEmployee(email: string, dto: UpdateEmployeeDto): Promise { - // 1) Tenter sur employés actifs - const active = await this.prisma.employees.findFirst({ - where: { user: { email } }, - include: { user: true }, - }); +// async patchEmployee(email: string, dto: UpdateEmployeeDto): Promise { +// // 1) Tenter sur employés actifs +// const active = await this.prisma.employees.findFirst({ +// where: { user: { email } }, +// include: { user: true }, +// }); - if (active) { - // Archivage : si on reçoit un last_work_day défini et que l'employé n’est pas déjà terminé - if (dto.last_work_day !== undefined && active.last_work_day == null && dto.last_work_day !== null) { - return this.archiveOnTermination(active, dto); - } +// if (active) { +// // Archivage : si on reçoit un last_work_day défini et que l'employé n’est pas déjà terminé +// if (dto.last_work_day !== undefined && active.last_work_day == null && dto.last_work_day !== null) { +// return this.archiveOnTermination(active, dto); +// } - // Sinon, update standard (split Users/Employees) - const { - first_name, - last_name, - email: new_email, - phone_number, - residence, - external_payroll_id, - company_code, - job_title, - first_work_day, - last_work_day, - supervisor_id, - is_supervisor, - } = dto as any; +// // Sinon, update standard (split Users/Employees) +// const { +// first_name, +// last_name, +// email: new_email, +// phone_number, +// residence, +// external_payroll_id, +// company_code, +// job_title, +// first_work_day, +// last_work_day, +// supervisor_id, +// is_supervisor, +// } = dto as any; - const first_work_d = toDateOrUndefined(first_work_day); - const last_work_d = Object.prototype.hasOwnProperty('last_work_day') - ? toDateOrNull(last_work_day ?? null) - : undefined; +// const first_work_d = toDateOrUndefined(first_work_day); +// const last_work_d = Object.prototype.hasOwnProperty('last_work_day') +// ? toDateOrNull(last_work_day ?? null) +// : undefined; - await this.prisma.$transaction(async (transaction) => { - if ( - first_name !== undefined || - last_name !== undefined || - new_email !== undefined || - phone_number !== undefined || - residence !== undefined - ) { - await transaction.users.update({ - where: { id: active.user_id }, - data: { - ...(first_name !== undefined ? { first_name } : {}), - ...(last_name !== undefined ? { last_name } : {}), - ...(email !== undefined ? { email: new_email } : {}), - ...(phone_number !== undefined ? { phone_number } : {}), - ...(residence !== undefined ? { residence } : {}), - }, - }); +// await this.prisma.$transaction(async (transaction) => { +// if ( +// first_name !== undefined || +// last_name !== undefined || +// new_email !== undefined || +// phone_number !== undefined || +// residence !== undefined +// ) { +// await transaction.users.update({ +// where: { id: active.user_id }, +// data: { +// ...(first_name !== undefined ? { first_name } : {}), +// ...(last_name !== undefined ? { last_name } : {}), +// ...(email !== undefined ? { email: new_email } : {}), +// ...(phone_number !== undefined ? { phone_number } : {}), +// ...(residence !== undefined ? { residence } : {}), +// }, +// }); - } +// } - const updated = await transaction.employees.update({ - where: { id: active.id }, - data: { - ...(external_payroll_id !== undefined ? { external_payroll_id } : {}), - ...(company_code !== undefined ? { company_code } : {}), - ...(job_title !== undefined ? { job_title } : {}), - ...(first_work_d !== undefined ? { first_work_day: first_work_d } : {}), - ...(last_work_d !== undefined ? { last_work_day: last_work_d } : {}), - ...(is_supervisor !== undefined ? { is_supervisor } : {}), - ...(supervisor_id !== undefined ? { supervisor_id } : {}), - }, - include: { user: true }, - }); +// const updated = await transaction.employees.update({ +// where: { id: active.id }, +// data: { +// ...(external_payroll_id !== undefined ? { external_payroll_id } : {}), +// ...(company_code !== undefined ? { company_code } : {}), +// ...(job_title !== undefined ? { job_title } : {}), +// ...(first_work_d !== undefined ? { first_work_day: first_work_d } : {}), +// ...(last_work_d !== undefined ? { last_work_day: last_work_d } : {}), +// ...(is_supervisor !== undefined ? { is_supervisor } : {}), +// ...(supervisor_id !== undefined ? { supervisor_id } : {}), +// }, +// include: { user: true }, +// }); - return updated; - }); +// return updated; +// }); - return this.prisma.employees.findFirst({ where: { user: { email } } }); - } +// return this.prisma.employees.findFirst({ where: { user: { email } } }); +// } - const user = await this.prisma.users.findUnique({ where: { email } }); - if (!user) return null; - // 2) Pas trouvé en actifs → regarder en archive (pour restauration) - const archived = await this.prisma.employeesArchive.findFirst({ - where: { user_id: user.id }, - include: { user: true }, - }); +// const user = await this.prisma.users.findUnique({ where: { email } }); +// if (!user) return null; +// // 2) Pas trouvé en actifs → regarder en archive (pour restauration) +// const archived = await this.prisma.employeesArchive.findFirst({ +// where: { user_id: user.id }, +// include: { user: true }, +// }); - if (archived) { - // Condition de restauration : last_work_day === null ou first_work_day fourni - const restore = dto.last_work_day === null || dto.first_work_day != null; - if (restore) { - return this.restoreEmployee(archived, dto); - } - } - // 3) Ni actif, ni archivé → 404 dans le controller - return null; - } +// if (archived) { +// // Condition de restauration : last_work_day === null ou first_work_day fourni +// const restore = dto.last_work_day === null || dto.first_work_day != null; +// if (restore) { +// return this.restoreEmployee(archived, dto); +// } +// } +// // 3) Ni actif, ni archivé → 404 dans le controller +// return null; +// } - //transfers the employee to archive and then delete from employees table - private async archiveOnTermination(active: Employees & { user: Users }, dto: UpdateEmployeeDto): Promise { - const last_work_d = toDateOrNull(dto.last_work_day!); - if (!last_work_d) throw new Error('invalide last_work_day for archive'); - return this.prisma.$transaction(async transaction => { - //detach crew from supervisor if employee is a supervisor - await transaction.employees.updateMany({ - where: { supervisor_id: active.id }, - data: { supervisor_id: null }, - }) - const archived = await transaction.employeesArchive.create({ - data: { - employee_id: active.id, - user_id: active.user_id, - first_name: active.user.first_name, - last_name: active.user.last_name, - company_code: active.company_code, - job_title: active.job_title, - first_work_day: active.first_work_day, - last_work_day: last_work_d, - supervisor_id: active.supervisor_id ?? null, - is_supervisor: active.is_supervisor, - external_payroll_id: active.external_payroll_id, - }, - include: { user: true } - }); - //delete from employees table - await transaction.employees.delete({ where: { id: active.id } }); - //return archived employee - return archived - }); - } +// //transfers the employee to archive and then delete from employees table +// private async archiveOnTermination(active: Employees & { user: Users }, dto: UpdateEmployeeDto): Promise { +// const last_work_d = toDateOrNull(dto.last_work_day!); +// if (!last_work_d) throw new Error('invalide last_work_day for archive'); +// return this.prisma.$transaction(async transaction => { +// //detach crew from supervisor if employee is a supervisor +// await transaction.employees.updateMany({ +// where: { supervisor_id: active.id }, +// data: { supervisor_id: null }, +// }) +// const archived = await transaction.employeesArchive.create({ +// data: { +// employee_id: active.id, +// user_id: active.user_id, +// first_name: active.user.first_name, +// last_name: active.user.last_name, +// company_code: active.company_code, +// job_title: active.job_title, +// first_work_day: active.first_work_day, +// last_work_day: last_work_d, +// supervisor_id: active.supervisor_id ?? null, +// is_supervisor: active.is_supervisor, +// external_payroll_id: active.external_payroll_id, +// }, +// include: { user: true } +// }); +// //delete from employees table +// await transaction.employees.delete({ where: { id: active.id } }); +// //return archived employee +// return archived +// }); +// } - //transfers the employee from archive to the employees table - private async restoreEmployee(archived: EmployeesArchive & { user: Users }, dto: UpdateEmployeeDto): Promise { - // const first_work_d = toDateOrUndefined(dto.first_work_day); - return this.prisma.$transaction(async transaction => { - //restores the archived employee into the employees table - const restored = await transaction.employees.create({ - data: { - user_id: archived.user_id, - company_code: archived.company_code, - job_title: archived.job_title, - first_work_day: archived.first_work_day, - last_work_day: null, - is_supervisor: archived.is_supervisor ?? false, - external_payroll_id: archived.external_payroll_id, - }, - }); - //deleting archived entry by id - await transaction.employeesArchive.delete({ where: { id: archived.id } }); +// //transfers the employee from archive to the employees table +// private async restoreEmployee(archived: EmployeesArchive & { user: Users }, dto: UpdateEmployeeDto): Promise { +// // const first_work_d = toDateOrUndefined(dto.first_work_day); +// return this.prisma.$transaction(async transaction => { +// //restores the archived employee into the employees table +// const restored = await transaction.employees.create({ +// data: { +// user_id: archived.user_id, +// company_code: archived.company_code, +// job_title: archived.job_title, +// first_work_day: archived.first_work_day, +// last_work_day: null, +// is_supervisor: archived.is_supervisor ?? false, +// external_payroll_id: archived.external_payroll_id, +// }, +// }); +// //deleting archived entry by id +// await transaction.employeesArchive.delete({ where: { id: archived.id } }); - //return restored employee - return restored; - }); - } +// //return restored employee +// return restored; +// }); +// } - //fetches all archived employees - async findAllArchived(): Promise { - return this.prisma.employeesArchive.findMany(); - } +// //fetches all archived employees +// async findAllArchived(): Promise { +// return this.prisma.employeesArchive.findMany(); +// } - //fetches an archived employee - async findOneArchived(id: number): Promise { - return this.prisma.employeesArchive.findUniqueOrThrow({ where: { id } }); - } +// //fetches an archived employee +// async findOneArchived(id: number): Promise { +// return this.prisma.employeesArchive.findUniqueOrThrow({ where: { id } }); +// } -} +// } diff --git a/src/modules/expenses/expenses.module.ts b/src/modules/expenses/expenses.module.ts deleted file mode 100644 index 85f62cb..0000000 --- a/src/modules/expenses/expenses.module.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ExpensesArchivalService } from "./services/expenses-archival.service"; -import { BusinessLogicsModule } from "src/modules/business-logics/business-logics.module"; -import { ExpenseUpsertService } from "src/modules/expenses/services/expense-upsert.service"; -import { ExpenseController } from "src/modules/expenses/controllers/expense.controller"; -import { SharedModule } from "../shared/shared.module"; -import { Module } from "@nestjs/common"; - -@Module({ - imports: [ BusinessLogicsModule, SharedModule ], - controllers: [ ExpenseController ], - providers: [ ExpenseUpsertService, ExpensesArchivalService ], - exports: [ ExpensesArchivalService ], -}) - -export class ExpensesModule {} \ No newline at end of file diff --git a/src/modules/exports/csv-exports.module.ts b/src/modules/exports/csv-exports.module.ts index 0c9f00b..fa0c7f1 100644 --- a/src/modules/exports/csv-exports.module.ts +++ b/src/modules/exports/csv-exports.module.ts @@ -1,7 +1,7 @@ import { Module } from "@nestjs/common"; import { CsvExportController } from "./controllers/csv-exports.controller"; import { CsvExportService } from "./services/csv-exports.service"; -import { SharedModule } from "../../Time_And_Attendance/modules/shared/shared.module"; +import { SharedModule } from "../../time-and-attendance/modules/shared/shared.module"; @Module({ providers:[CsvExportService, SharedModule], diff --git a/src/modules/preferences/preferences.module.ts b/src/modules/preferences/preferences.module.ts index 59031ba..65deb82 100644 --- a/src/modules/preferences/preferences.module.ts +++ b/src/modules/preferences/preferences.module.ts @@ -1,7 +1,7 @@ import { Module } from "@nestjs/common"; import { PreferencesController } from "./controllers/preferences.controller"; import { PreferencesService } from "./services/preferences.service"; -import { SharedModule } from "../../Time_And_Attendance/modules/shared/shared.module"; +import { SharedModule } from "../../time-and-attendance/modules/shared/shared.module"; @Module({ imports: [SharedModule], diff --git a/src/modules/preferences/services/preferences.service.ts b/src/modules/preferences/services/preferences.service.ts index 2677573..2ff02ac 100644 --- a/src/modules/preferences/services/preferences.service.ts +++ b/src/modules/preferences/services/preferences.service.ts @@ -2,7 +2,7 @@ import { Injectable } from "@nestjs/common"; import { Preferences } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; import { PreferencesDto } from "../dtos/preferences.dto"; -import { EmailToIdResolver } from "src/Time_And_Attendance/modules/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; @Injectable() export class PreferencesService { diff --git a/src/modules/timesheets/timesheets.module.ts b/src/modules/timesheets/timesheets.module.ts deleted file mode 100644 index e1c997e..0000000 --- a/src/modules/timesheets/timesheets.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { GetTimesheetsOverviewService } from '../../Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service'; -import { TimesheetArchiveService } from '../../Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-archive.service'; -import { BusinessLogicsModule } from 'src/modules/business-logics/business-logics.module'; -import { TimesheetController } from '../../Time_And_Attendance/modules/time-tracker/timesheets/controllers/timesheet.controller'; -import { SharedModule } from '../../Time_And_Attendance/modules/shared/shared.module'; -import { ShiftsModule } from '../../Time_And_Attendance/modules/time-tracker/shifts/shifts.module'; -import { Module } from '@nestjs/common'; - -@Module({ - imports: [ - BusinessLogicsModule, - SharedModule, - ShiftsModule, - ], - controllers: [TimesheetController], - providers: [ - TimesheetArchiveService, - GetTimesheetsOverviewService, - SharedModule, - ], - exports: [], -}) -export class TimesheetsModule {} diff --git a/src/Time_And_Attendance/domains/business-logics.module.ts b/src/time-and-attendance/domains/business-logics.module.ts similarity index 100% rename from src/Time_And_Attendance/domains/business-logics.module.ts rename to src/time-and-attendance/domains/business-logics.module.ts diff --git a/src/Time_And_Attendance/domains/services/after-hours.service.ts b/src/time-and-attendance/domains/services/after-hours.service.ts similarity index 100% rename from src/Time_And_Attendance/domains/services/after-hours.service.ts rename to src/time-and-attendance/domains/services/after-hours.service.ts diff --git a/src/Time_And_Attendance/domains/services/holiday.service.ts b/src/time-and-attendance/domains/services/holiday.service.ts similarity index 100% rename from src/Time_And_Attendance/domains/services/holiday.service.ts rename to src/time-and-attendance/domains/services/holiday.service.ts diff --git a/src/Time_And_Attendance/domains/services/mileage.service.ts b/src/time-and-attendance/domains/services/mileage.service.ts similarity index 100% rename from src/Time_And_Attendance/domains/services/mileage.service.ts rename to src/time-and-attendance/domains/services/mileage.service.ts diff --git a/src/Time_And_Attendance/domains/services/overtime.service.ts b/src/time-and-attendance/domains/services/overtime.service.ts similarity index 100% rename from src/Time_And_Attendance/domains/services/overtime.service.ts rename to src/time-and-attendance/domains/services/overtime.service.ts diff --git a/src/Time_And_Attendance/domains/services/sick-leave.service.ts b/src/time-and-attendance/domains/services/sick-leave.service.ts similarity index 100% rename from src/Time_And_Attendance/domains/services/sick-leave.service.ts rename to src/time-and-attendance/domains/services/sick-leave.service.ts diff --git a/src/Time_And_Attendance/domains/services/vacation.service.ts b/src/time-and-attendance/domains/services/vacation.service.ts similarity index 100% rename from src/Time_And_Attendance/domains/services/vacation.service.ts rename to src/time-and-attendance/domains/services/vacation.service.ts diff --git a/src/modules/expenses/controllers/expense.controller.ts b/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts similarity index 91% rename from src/modules/expenses/controllers/expense.controller.ts rename to src/time-and-attendance/modules/expenses/controllers/expense.controller.ts index e5e5f56..b7a3736 100644 --- a/src/modules/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts @@ -2,7 +2,7 @@ import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from import { PrismaService } from "src/prisma/prisma.service"; import { ExpenseDto } from "../dtos/expense.dto"; import { CreateResult, ExpenseUpsertService, UpdateResult } from "../services/expense-upsert.service"; -import { updateExpenseDto } from "src/modules/expenses/dtos/update-expense.dto"; +import { updateExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/update-expense.dto"; @Controller('expense') diff --git a/src/modules/expenses/dtos/expense.dto.ts b/src/time-and-attendance/modules/expenses/dtos/expense.dto.ts similarity index 100% rename from src/modules/expenses/dtos/expense.dto.ts rename to src/time-and-attendance/modules/expenses/dtos/expense.dto.ts diff --git a/src/modules/expenses/dtos/get-expense.dto.ts b/src/time-and-attendance/modules/expenses/dtos/get-expense.dto.ts similarity index 100% rename from src/modules/expenses/dtos/get-expense.dto.ts rename to src/time-and-attendance/modules/expenses/dtos/get-expense.dto.ts diff --git a/src/modules/expenses/dtos/update-expense.dto.ts b/src/time-and-attendance/modules/expenses/dtos/update-expense.dto.ts similarity index 100% rename from src/modules/expenses/dtos/update-expense.dto.ts rename to src/time-and-attendance/modules/expenses/dtos/update-expense.dto.ts diff --git a/src/time-and-attendance/modules/expenses/expenses.module.ts b/src/time-and-attendance/modules/expenses/expenses.module.ts new file mode 100644 index 0000000..923ce3e --- /dev/null +++ b/src/time-and-attendance/modules/expenses/expenses.module.ts @@ -0,0 +1,15 @@ +import { ExpensesArchivalService } from "./services/expenses-archival.service"; +import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; +import { ExpenseController } from "src/time-and-attendance/modules/expenses/controllers/expense.controller"; +import { Module } from "@nestjs/common"; +import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; +import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; + +@Module({ + imports: [ BusinessLogicsModule, SharedModule ], + controllers: [ ExpenseController ], + providers: [ ExpenseUpsertService, ExpensesArchivalService ], + exports: [ ExpensesArchivalService ], +}) + +export class ExpensesModule {} \ No newline at end of file diff --git a/src/modules/expenses/helpers/expenses-date-time-helpers.ts b/src/time-and-attendance/modules/expenses/helpers/expenses-date-time-helpers.ts similarity index 100% rename from src/modules/expenses/helpers/expenses-date-time-helpers.ts rename to src/time-and-attendance/modules/expenses/helpers/expenses-date-time-helpers.ts diff --git a/src/modules/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts similarity index 100% rename from src/modules/expenses/services/expense-upsert.service.ts rename to src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts diff --git a/src/modules/expenses/services/expenses-archival.service.ts b/src/time-and-attendance/modules/expenses/services/expenses-archival.service.ts similarity index 100% rename from src/modules/expenses/services/expenses-archival.service.ts rename to src/time-and-attendance/modules/expenses/services/expenses-archival.service.ts diff --git a/src/modules/expenses/~misc_deprecated-files/create-expense.dto.ts b/src/time-and-attendance/modules/expenses/~misc_deprecated-files/create-expense.dto.ts similarity index 100% rename from src/modules/expenses/~misc_deprecated-files/create-expense.dto.ts rename to src/time-and-attendance/modules/expenses/~misc_deprecated-files/create-expense.dto.ts diff --git a/src/modules/expenses/~misc_deprecated-files/expenses-command.service.ts b/src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses-command.service.ts similarity index 100% rename from src/modules/expenses/~misc_deprecated-files/expenses-command.service.ts rename to src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses-command.service.ts diff --git a/src/modules/expenses/~misc_deprecated-files/expenses-query.service.ts b/src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses-query.service.ts similarity index 100% rename from src/modules/expenses/~misc_deprecated-files/expenses-query.service.ts rename to src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses-query.service.ts diff --git a/src/modules/expenses/~misc_deprecated-files/expenses.controller.ts b/src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses.controller.ts similarity index 100% rename from src/modules/expenses/~misc_deprecated-files/expenses.controller.ts rename to src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses.controller.ts diff --git a/src/modules/expenses/~misc_deprecated-files/expenses.types.interfaces.ts b/src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses.types.interfaces.ts similarity index 100% rename from src/modules/expenses/~misc_deprecated-files/expenses.types.interfaces.ts rename to src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses.types.interfaces.ts diff --git a/src/modules/expenses/~misc_deprecated-files/expenses.utils.ts b/src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses.utils.ts similarity index 100% rename from src/modules/expenses/~misc_deprecated-files/expenses.utils.ts rename to src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses.utils.ts diff --git a/src/modules/expenses/~misc_deprecated-files/search-expense.dto.ts b/src/time-and-attendance/modules/expenses/~misc_deprecated-files/search-expense.dto.ts similarity index 100% rename from src/modules/expenses/~misc_deprecated-files/search-expense.dto.ts rename to src/time-and-attendance/modules/expenses/~misc_deprecated-files/search-expense.dto.ts diff --git a/src/modules/expenses/~misc_deprecated-files/update-expense.dto.ts b/src/time-and-attendance/modules/expenses/~misc_deprecated-files/update-expense.dto.ts similarity index 100% rename from src/modules/expenses/~misc_deprecated-files/update-expense.dto.ts rename to src/time-and-attendance/modules/expenses/~misc_deprecated-files/update-expense.dto.ts diff --git a/src/modules/expenses/~misc_deprecated-files/upsert-expense.dto.ts b/src/time-and-attendance/modules/expenses/~misc_deprecated-files/upsert-expense.dto.ts similarity index 100% rename from src/modules/expenses/~misc_deprecated-files/upsert-expense.dto.ts rename to src/time-and-attendance/modules/expenses/~misc_deprecated-files/upsert-expense.dto.ts diff --git a/src/Time_And_Attendance/modules/leave-requests/controllers/leave-requests.controller.ts b/src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller.ts similarity index 100% rename from src/Time_And_Attendance/modules/leave-requests/controllers/leave-requests.controller.ts rename to src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller.ts diff --git a/src/Time_And_Attendance/modules/leave-requests/dtos/leave-request-view.dto.ts b/src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/leave-requests/dtos/leave-request-view.dto.ts rename to src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto.ts diff --git a/src/Time_And_Attendance/modules/leave-requests/dtos/upsert-leave-request.dto.ts b/src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/leave-requests/dtos/upsert-leave-request.dto.ts rename to src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto.ts diff --git a/src/Time_And_Attendance/modules/leave-requests/leave-requests.module.ts b/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts similarity index 100% rename from src/Time_And_Attendance/modules/leave-requests/leave-requests.module.ts rename to src/time-and-attendance/modules/leave-requests/leave-requests.module.ts diff --git a/src/Time_And_Attendance/modules/leave-requests/mappers/leave-requests-archive.mapper.ts b/src/time-and-attendance/modules/leave-requests/mappers/leave-requests-archive.mapper.ts similarity index 100% rename from src/Time_And_Attendance/modules/leave-requests/mappers/leave-requests-archive.mapper.ts rename to src/time-and-attendance/modules/leave-requests/mappers/leave-requests-archive.mapper.ts diff --git a/src/Time_And_Attendance/modules/leave-requests/mappers/leave-requests.mapper.ts b/src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper.ts similarity index 100% rename from src/Time_And_Attendance/modules/leave-requests/mappers/leave-requests.mapper.ts rename to src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper.ts diff --git a/src/Time_And_Attendance/modules/leave-requests/services/holiday-leave-requests.service.ts b/src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/leave-requests/services/holiday-leave-requests.service.ts rename to src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts diff --git a/src/Time_And_Attendance/modules/leave-requests/services/leave-request.service.ts b/src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/leave-requests/services/leave-request.service.ts rename to src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts diff --git a/src/Time_And_Attendance/modules/leave-requests/services/sick-leave-requests.service.ts b/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/leave-requests/services/sick-leave-requests.service.ts rename to src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts diff --git a/src/Time_And_Attendance/modules/leave-requests/services/vacation-leave-requests.service.ts b/src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/leave-requests/services/vacation-leave-requests.service.ts rename to src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts diff --git a/src/Time_And_Attendance/modules/leave-requests/utils/leave-request.transform.ts b/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts similarity index 100% rename from src/Time_And_Attendance/modules/leave-requests/utils/leave-request.transform.ts rename to src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts diff --git a/src/Time_And_Attendance/modules/leave-requests/utils/leave-request.util.ts b/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts similarity index 100% rename from src/Time_And_Attendance/modules/leave-requests/utils/leave-request.util.ts rename to src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts diff --git a/src/Time_And_Attendance/modules/leave-requests/utils/leave-requests-archive.select.ts b/src/time-and-attendance/modules/leave-requests/utils/leave-requests-archive.select.ts similarity index 100% rename from src/Time_And_Attendance/modules/leave-requests/utils/leave-requests-archive.select.ts rename to src/time-and-attendance/modules/leave-requests/utils/leave-requests-archive.select.ts diff --git a/src/Time_And_Attendance/modules/leave-requests/utils/leave-requests.select.ts b/src/time-and-attendance/modules/leave-requests/utils/leave-requests.select.ts similarity index 100% rename from src/Time_And_Attendance/modules/leave-requests/utils/leave-requests.select.ts rename to src/time-and-attendance/modules/leave-requests/utils/leave-requests.select.ts diff --git a/src/Time_And_Attendance/modules/pay-period/controllers/pay-periods.controller.ts b/src/time-and-attendance/modules/pay-period/controllers/pay-periods.controller.ts similarity index 100% rename from src/Time_And_Attendance/modules/pay-period/controllers/pay-periods.controller.ts rename to src/time-and-attendance/modules/pay-period/controllers/pay-periods.controller.ts diff --git a/src/Time_And_Attendance/modules/pay-period/dtos/bulk-crew-approval.dto.ts b/src/time-and-attendance/modules/pay-period/dtos/bulk-crew-approval.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/pay-period/dtos/bulk-crew-approval.dto.ts rename to src/time-and-attendance/modules/pay-period/dtos/bulk-crew-approval.dto.ts diff --git a/src/Time_And_Attendance/modules/pay-period/dtos/bundle-pay-period.dto.ts b/src/time-and-attendance/modules/pay-period/dtos/bundle-pay-period.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/pay-period/dtos/bundle-pay-period.dto.ts rename to src/time-and-attendance/modules/pay-period/dtos/bundle-pay-period.dto.ts diff --git a/src/Time_And_Attendance/modules/pay-period/dtos/overview-employee-period.dto.ts b/src/time-and-attendance/modules/pay-period/dtos/overview-employee-period.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/pay-period/dtos/overview-employee-period.dto.ts rename to src/time-and-attendance/modules/pay-period/dtos/overview-employee-period.dto.ts diff --git a/src/Time_And_Attendance/modules/pay-period/dtos/overview-pay-period.dto.ts b/src/time-and-attendance/modules/pay-period/dtos/overview-pay-period.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/pay-period/dtos/overview-pay-period.dto.ts rename to src/time-and-attendance/modules/pay-period/dtos/overview-pay-period.dto.ts diff --git a/src/Time_And_Attendance/modules/pay-period/dtos/pay-period.dto.ts b/src/time-and-attendance/modules/pay-period/dtos/pay-period.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/pay-period/dtos/pay-period.dto.ts rename to src/time-and-attendance/modules/pay-period/dtos/pay-period.dto.ts diff --git a/src/Time_And_Attendance/modules/pay-period/mappers/pay-periods.mapper.ts b/src/time-and-attendance/modules/pay-period/mappers/pay-periods.mapper.ts similarity index 100% rename from src/Time_And_Attendance/modules/pay-period/mappers/pay-periods.mapper.ts rename to src/time-and-attendance/modules/pay-period/mappers/pay-periods.mapper.ts diff --git a/src/Time_And_Attendance/modules/pay-period/pay-periods.module.ts b/src/time-and-attendance/modules/pay-period/pay-periods.module.ts similarity index 68% rename from src/Time_And_Attendance/modules/pay-period/pay-periods.module.ts rename to src/time-and-attendance/modules/pay-period/pay-periods.module.ts index fabd257..6e15221 100644 --- a/src/Time_And_Attendance/modules/pay-period/pay-periods.module.ts +++ b/src/time-and-attendance/modules/pay-period/pay-periods.module.ts @@ -1,14 +1,13 @@ -import { PrismaModule } from "src/prisma/prisma.module"; -import { PayPeriodsController } from "./controllers/pay-periods.controller"; -import { Module } from "@nestjs/common"; import { PayPeriodsQueryService } from "./services/pay-periods-query.service"; -import { TimesheetsModule } from "../time-tracker/timesheets/timesheets.module"; -import { SharedModule } from "../shared/shared.module"; +import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; +import { PayPeriodsController } from "./controllers/pay-periods.controller"; import { PrismaService } from "src/prisma/prisma.service"; -import { BusinessLogicsModule } from "../../../modules/business-logics/business-logics.module"; +import { PrismaModule } from "src/prisma/prisma.module"; +import { SharedModule } from "../shared/shared.module"; +import { Module } from "@nestjs/common"; @Module({ - imports: [PrismaModule, TimesheetsModule, SharedModule, BusinessLogicsModule], + imports: [PrismaModule, SharedModule, BusinessLogicsModule], providers: [ PayPeriodsQueryService, PrismaService, diff --git a/src/Time_And_Attendance/modules/pay-period/services/pay-periods-command.service.ts b/src/time-and-attendance/modules/pay-period/services/pay-periods-command.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/pay-period/services/pay-periods-command.service.ts rename to src/time-and-attendance/modules/pay-period/services/pay-periods-command.service.ts diff --git a/src/Time_And_Attendance/modules/pay-period/services/pay-periods-query.service.ts b/src/time-and-attendance/modules/pay-period/services/pay-periods-query.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/pay-period/services/pay-periods-query.service.ts rename to src/time-and-attendance/modules/pay-period/services/pay-periods-query.service.ts diff --git a/src/Time_And_Attendance/modules/pay-period/utils/pay-year.util.ts b/src/time-and-attendance/modules/pay-period/utils/pay-year.util.ts similarity index 100% rename from src/Time_And_Attendance/modules/pay-period/utils/pay-year.util.ts rename to src/time-and-attendance/modules/pay-period/utils/pay-year.util.ts diff --git a/src/Time_And_Attendance/modules/shared/constants/date-time.constant.ts b/src/time-and-attendance/modules/shared/constants/date-time.constant.ts similarity index 100% rename from src/Time_And_Attendance/modules/shared/constants/date-time.constant.ts rename to src/time-and-attendance/modules/shared/constants/date-time.constant.ts diff --git a/src/Time_And_Attendance/modules/shared/constants/regex.constant.ts b/src/time-and-attendance/modules/shared/constants/regex.constant.ts similarity index 100% rename from src/Time_And_Attendance/modules/shared/constants/regex.constant.ts rename to src/time-and-attendance/modules/shared/constants/regex.constant.ts diff --git a/src/Time_And_Attendance/modules/shared/constants/utils.constant.ts b/src/time-and-attendance/modules/shared/constants/utils.constant.ts similarity index 100% rename from src/Time_And_Attendance/modules/shared/constants/utils.constant.ts rename to src/time-and-attendance/modules/shared/constants/utils.constant.ts diff --git a/src/Time_And_Attendance/modules/shared/helpers/date-time.helpers.ts b/src/time-and-attendance/modules/shared/helpers/date-time.helpers.ts similarity index 100% rename from src/Time_And_Attendance/modules/shared/helpers/date-time.helpers.ts rename to src/time-and-attendance/modules/shared/helpers/date-time.helpers.ts diff --git a/src/Time_And_Attendance/modules/shared/interfaces/shifts.interface.ts b/src/time-and-attendance/modules/shared/interfaces/shifts.interface.ts similarity index 100% rename from src/Time_And_Attendance/modules/shared/interfaces/shifts.interface.ts rename to src/time-and-attendance/modules/shared/interfaces/shifts.interface.ts diff --git a/src/Time_And_Attendance/modules/shared/selects/expenses.select.ts b/src/time-and-attendance/modules/shared/selects/expenses.select.ts similarity index 100% rename from src/Time_And_Attendance/modules/shared/selects/expenses.select.ts rename to src/time-and-attendance/modules/shared/selects/expenses.select.ts diff --git a/src/Time_And_Attendance/modules/shared/selects/pay-periods.select.ts b/src/time-and-attendance/modules/shared/selects/pay-periods.select.ts similarity index 100% rename from src/Time_And_Attendance/modules/shared/selects/pay-periods.select.ts rename to src/time-and-attendance/modules/shared/selects/pay-periods.select.ts diff --git a/src/Time_And_Attendance/modules/shared/selects/shifts.select.ts b/src/time-and-attendance/modules/shared/selects/shifts.select.ts similarity index 100% rename from src/Time_And_Attendance/modules/shared/selects/shifts.select.ts rename to src/time-and-attendance/modules/shared/selects/shifts.select.ts diff --git a/src/Time_And_Attendance/modules/shared/shared.module.ts b/src/time-and-attendance/modules/shared/shared.module.ts similarity index 100% rename from src/Time_And_Attendance/modules/shared/shared.module.ts rename to src/time-and-attendance/modules/shared/shared.module.ts diff --git a/src/Time_And_Attendance/modules/shared/types/upsert-actions.types.ts b/src/time-and-attendance/modules/shared/types/upsert-actions.types.ts similarity index 100% rename from src/Time_And_Attendance/modules/shared/types/upsert-actions.types.ts rename to src/time-and-attendance/modules/shared/types/upsert-actions.types.ts diff --git a/src/Time_And_Attendance/modules/shared/utils/resolve-bank-type-id.utils.ts b/src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils.ts similarity index 100% rename from src/Time_And_Attendance/modules/shared/utils/resolve-bank-type-id.utils.ts rename to src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils.ts diff --git a/src/Time_And_Attendance/modules/shared/utils/resolve-email-id.utils.ts b/src/time-and-attendance/modules/shared/utils/resolve-email-id.utils.ts similarity index 100% rename from src/Time_And_Attendance/modules/shared/utils/resolve-email-id.utils.ts rename to src/time-and-attendance/modules/shared/utils/resolve-email-id.utils.ts diff --git a/src/Time_And_Attendance/modules/shared/utils/resolve-full-name.utils.ts b/src/time-and-attendance/modules/shared/utils/resolve-full-name.utils.ts similarity index 100% rename from src/Time_And_Attendance/modules/shared/utils/resolve-full-name.utils.ts rename to src/time-and-attendance/modules/shared/utils/resolve-full-name.utils.ts diff --git a/src/Time_And_Attendance/modules/shared/utils/resolve-shifts-id.utils.ts b/src/time-and-attendance/modules/shared/utils/resolve-shifts-id.utils.ts similarity index 100% rename from src/Time_And_Attendance/modules/shared/utils/resolve-shifts-id.utils.ts rename to src/time-and-attendance/modules/shared/utils/resolve-shifts-id.utils.ts diff --git a/src/Time_And_Attendance/modules/shared/utils/resolve-timesheet.utils.ts b/src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts similarity index 94% rename from src/Time_And_Attendance/modules/shared/utils/resolve-timesheet.utils.ts rename to src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts index 2c5e515..11c89f2 100644 --- a/src/Time_And_Attendance/modules/shared/utils/resolve-timesheet.utils.ts +++ b/src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts @@ -1,6 +1,6 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { Prisma, PrismaClient } from "@prisma/client"; -import { weekStartSunday } from "src/Time_And_Attendance/modules/time-tracker/shifts/helpers/shifts-date-time-helpers"; +import { weekStartSunday } from "src/time-and-attendance/modules/time-tracker/shifts/helpers/shifts-date-time-helpers"; import { PrismaService } from "src/prisma/prisma.service"; import { EmailToIdResolver } from "./resolve-email-id.utils"; diff --git a/src/Time_And_Attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts similarity index 96% rename from src/Time_And_Attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts rename to src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index 0efb934..79a8976 100644 --- a/src/Time_And_Attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -1,7 +1,7 @@ import { BadRequestException, Body, Controller, Get, NotFoundException, Param, Post, Put, Query } from "@nestjs/common"; import { SchedulePresetsDto } from "../dtos/create-schedule-presets.dto"; import { SchedulePresetsCommandService } from "../services/schedule-presets-command.service"; -import { UpsertAction } from "src/Time_And_Attendance/modules/shared/types/upsert-actions.types"; +import { UpsertAction } from "src/time-and-attendance/modules/shared/types/upsert-actions.types"; import { SchedulePresetsQueryService } from "../services/schedule-presets-query.service"; @Controller('schedule-presets') diff --git a/src/Time_And_Attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts rename to src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts rename to src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/schedule-presets/mappers/schedule-presets.mappers.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/mappers/schedule-presets.mappers.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/schedule-presets/mappers/schedule-presets.mappers.ts rename to src/time-and-attendance/modules/time-tracker/schedule-presets/mappers/schedule-presets.mappers.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts similarity index 91% rename from src/Time_And_Attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts rename to src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts index 2e25a6d..8194655 100644 --- a/src/Time_And_Attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts @@ -4,7 +4,7 @@ import { SchedulePresetsQueryService } from "./services/schedule-presets-query.s import { SchedulePresetsController } from "./controller/schedule-presets.controller"; import { PrismaService } from "src/prisma/prisma.service"; import { SchedulePresetsApplyService } from "./services/schedule-presets-apply.service"; -import { SharedModule } from "../shared/shared.module"; +import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; @Module({ imports: [SharedModule], diff --git a/src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts similarity index 98% rename from src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts rename to src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts index e2e8dbf..98bde74 100644 --- a/src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts @@ -1,5 +1,5 @@ import { BadRequestException, ConflictException, Injectable, NotFoundException } from "@nestjs/common"; -import { EmailToIdResolver } from "src/Time_And_Attendance/modules/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { ApplyResult } from "../types/schedule-presets.types"; import { Prisma, Weekday } from "@prisma/client"; diff --git a/src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts similarity index 98% rename from src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts rename to src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts index 5c1cb2c..3803004 100644 --- a/src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts @@ -1,7 +1,7 @@ import { BadRequestException, ConflictException, Injectable, NotFoundException } from "@nestjs/common"; -import { BankCodesResolver } from "src/Time_And_Attendance/modules/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/Time_And_Attendance/modules/shared/utils/resolve-email-id.utils"; -import { UpsertAction } from "src/Time_And_Attendance/modules/shared/types/upsert-actions.types"; +import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { UpsertAction } from "src/time-and-attendance/modules/shared/types/upsert-actions.types"; import { PrismaService } from "src/prisma/prisma.service"; import { SchedulePresetsDto } from "../dtos/create-schedule-presets.dto"; import { Prisma, Weekday } from "@prisma/client"; diff --git a/src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts similarity index 96% rename from src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts rename to src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts index 7b87cef..e406151 100644 --- a/src/Time_And_Attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts @@ -1,5 +1,5 @@ import { Injectable, NotFoundException } from "@nestjs/common"; -import { EmailToIdResolver } from "src/Time_And_Attendance/modules/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { PresetResponse, ShiftResponse } from "../types/schedule-presets.types"; import { Prisma } from "@prisma/client"; diff --git a/src/Time_And_Attendance/modules/time-tracker/schedule-presets/types/schedule-presets.types.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/types/schedule-presets.types.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/schedule-presets/types/schedule-presets.types.ts rename to src/time-and-attendance/modules/time-tracker/schedule-presets/types/schedule-presets.types.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/controllers/shift.controller.ts rename to src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/dtos/get-shift.dto.ts b/src/time-and-attendance/modules/time-tracker/shifts/dtos/get-shift.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/dtos/get-shift.dto.ts rename to src/time-and-attendance/modules/time-tracker/shifts/dtos/get-shift.dto.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/dtos/shift.dto.ts b/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/dtos/shift.dto.ts rename to src/time-and-attendance/modules/time-tracker/shifts/dtos/shift.dto.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/dtos/update-shift.dto.ts b/src/time-and-attendance/modules/time-tracker/shifts/dtos/update-shift.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/dtos/update-shift.dto.ts rename to src/time-and-attendance/modules/time-tracker/shifts/dtos/update-shift.dto.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/helpers/shifts-date-time-helpers.ts b/src/time-and-attendance/modules/time-tracker/shifts/helpers/shifts-date-time-helpers.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/helpers/shifts-date-time-helpers.ts rename to src/time-and-attendance/modules/time-tracker/shifts/helpers/shifts-date-time-helpers.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts rename to src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-get.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-get.service.ts rename to src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts similarity index 99% rename from src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts rename to src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts index cc0e790..a7ec458 100644 --- a/src/Time_And_Attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts @@ -1,6 +1,6 @@ import { toDateFromString, toHHmmFromString, toStringFromDate, toStringFromHHmm } from "../helpers/shifts-date-time-helpers"; import { BadRequestException, ConflictException, Injectable, NotFoundException } from "@nestjs/common"; -import { OvertimeService, WeekOvertimeSummary } from "src/modules/business-logics/services/overtime.service"; +import { OvertimeService, WeekOvertimeSummary } from "src/time-and-attendance/domains/services/overtime.service"; import { updateShiftDto } from "../dtos/update-shift.dto"; import { PrismaService } from "src/prisma/prisma.service"; import { GetShiftDto } from "../dtos/get-shift.dto"; diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/shifts.module.ts b/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts similarity index 89% rename from src/Time_And_Attendance/modules/time-tracker/shifts/shifts.module.ts rename to src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts index 5d33c35..00fe148 100644 --- a/src/Time_And_Attendance/modules/time-tracker/shifts/shifts.module.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts @@ -1,5 +1,5 @@ import { ShiftsArchivalService } from './services/shifts-archival.service'; -import { BusinessLogicsModule } from 'src/modules/business-logics/business-logics.module'; +import { BusinessLogicsModule } from 'src/time-and-attendance/domains/business-logics.module'; import { NotificationsModule } from '../../../../modules/notifications/notifications.module'; import { ShiftsUpsertService } from './services/shifts-upsert.service'; import { ShiftsGetService } from './services/shifts-get.service'; diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/get-shift-overview.dto.ts b/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/get-shift-overview.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/get-shift-overview.dto.ts rename to src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/get-shift-overview.dto.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-command.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-command.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-command.service.ts rename to src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-command.service.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-overview-row.interface.ts b/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-overview-row.interface.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-overview-row.interface.ts rename to src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-overview-row.interface.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-query.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-query.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-query.service.ts rename to src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-query.service.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-upsert.types.ts b/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-upsert.types.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-upsert.types.ts rename to src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-upsert.types.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.controller.ts b/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.controller.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.controller.ts rename to src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.controller.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.helpers.ts b/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.helpers.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.helpers.ts rename to src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.helpers.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.utils.ts b/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.utils.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.utils.ts rename to src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.utils.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/upsert-shift.dto.ts b/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/upsert-shift.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/shifts/~misc_deprecated-files/upsert-shift.dto.ts rename to src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/upsert-shift.dto.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts similarity index 94% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts index 61eef3a..7eb7209 100644 --- a/src/Time_And_Attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts @@ -1,4 +1,4 @@ -import { EmailToIdResolver } from "src/Time_And_Attendance/modules/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; import { GetTimesheetsOverviewService } from "../services/timesheet-get-overview.service"; import { BadRequestException, Controller, Get, Query} from "@nestjs/common"; diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/dtos/timesheet.dto.ts b/src/time-and-attendance/modules/time-tracker/timesheets/dtos/timesheet.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/dtos/timesheet.dto.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/dtos/timesheet.dto.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/helpers/timesheets-date-time-helpers.ts b/src/time-and-attendance/modules/time-tracker/timesheets/helpers/timesheets-date-time-helpers.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/helpers/timesheets-date-time-helpers.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/helpers/timesheets-date-time-helpers.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-approval.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-approval.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-approval.service.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-approval.service.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-archive.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-archive.service.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/timesheets.module.ts b/src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts similarity index 70% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/timesheets.module.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts index 001fb19..930c70f 100644 --- a/src/Time_And_Attendance/modules/time-tracker/timesheets/timesheets.module.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts @@ -1,17 +1,11 @@ import { GetTimesheetsOverviewService } from './services/timesheet-get-overview.service'; import { TimesheetArchiveService } from './services/timesheet-archive.service'; -import { BusinessLogicsModule } from 'src/modules/business-logics/business-logics.module'; import { TimesheetController } from './controllers/timesheet.controller'; import { SharedModule } from '../../shared/shared.module'; -import { ShiftsModule } from '../shifts/shifts.module'; import { Module } from '@nestjs/common'; @Module({ - imports: [ - BusinessLogicsModule, - SharedModule, - ShiftsModule, - ], + imports: [], controllers: [TimesheetController], providers: [ TimesheetArchiveService, diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/create-timesheet.dto.ts b/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/create-timesheet.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/create-timesheet.dto.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/create-timesheet.dto.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/search-timesheet.dto.ts b/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/search-timesheet.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/search-timesheet.dto.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/search-timesheet.dto.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet-period.dto.ts b/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet-period.dto.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet-period.dto.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet-period.dto.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.helpers.ts b/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.helpers.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.helpers.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.helpers.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.mappers.ts b/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.mappers.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.mappers.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.mappers.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.selectors.ts b/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.selectors.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.selectors.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.selectors.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.types.ts b/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.types.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.types.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.types.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.utils.ts b/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.utils.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.utils.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.utils.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-command.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-command.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-command.service.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-command.service.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-query.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-query.service.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-query.service.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-query.service.ts diff --git a/src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets.controller.ts b/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets.controller.ts similarity index 100% rename from src/Time_And_Attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets.controller.ts rename to src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets.controller.ts diff --git a/src/time-and-attendance/time-and-attendance.module.ts b/src/time-and-attendance/time-and-attendance.module.ts new file mode 100644 index 0000000..ebcbd05 --- /dev/null +++ b/src/time-and-attendance/time-and-attendance.module.ts @@ -0,0 +1,42 @@ +import { Module } from "@nestjs/common"; +import { ExpensesArchiveController } from "src/modules/archival/controllers/expenses-archive.controller"; +import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; +import { ExpenseController } from "src/time-and-attendance/modules/expenses/controllers/expense.controller"; +import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; +import { ExpensesArchivalService } from "src/time-and-attendance/modules/expenses/services/expenses-archival.service"; +import { PayperiodsModule } from "src/time-and-attendance/modules/pay-period/pay-periods.module"; +import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller"; +import { SchedulePresetsCommandService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service"; +import { SchedulePresetsQueryService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service"; +import { ShiftController } from "src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller"; +import { ShiftsGetService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service"; +import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; +import { TimesheetController } from "src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller"; +import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service"; + +@Module({ + imports: [BusinessLogicsModule, PayperiodsModule], + controllers: [ + TimesheetController, + ShiftController, + SchedulePresetsController, + ExpenseController, + ExpensesArchiveController, + // LeaveRequestController, + + ], + providers: [ + GetTimesheetsOverviewService, + ShiftsGetService, + ShiftsUpsertService, + ExpenseUpsertService, + ExpensesArchivalService, + EmailToIdResolver, + SchedulePresetsCommandService, + BankCodesResolver, + SchedulePresetsQueryService, + ], + exports: [], +}) export class TimeAndAttendanceModule{}; \ No newline at end of file From 2712033451ae41958a24d98bb5e86a5d0dc75d64 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 24 Oct 2025 16:47:30 -0400 Subject: [PATCH 05/57] refactor(cleaning): preparing other modules for refactoring --- prisma/schema.prisma | 72 +-- src/app.module.ts | 4 +- .../expenses-archive.controller.ts | 58 +-- .../leave-requests-archive.controller.ts | 12 +- .../controllers/shifts-archive.controller.ts | 58 +-- .../timesheets-archive.controller.ts | 58 +-- .../archival/services/archival.service.ts | 66 +-- .../controllers/attachments.controller.ts | 4 +- src/modules/bank-codes/bank-codes.module.ts | 18 +- .../controllers/bank-codes.controller.ts | 86 ++-- .../bank-codes/dtos/create-bank-code.dto.ts | 82 ++-- .../bank-codes/dtos/update-bank-code.dto.ts | 6 +- .../bank-codes/services/bank-codes.service.ts | 84 ++-- .../customers/dtos/create-customer.dto.ts | 142 +++--- .../customers/dtos/update-customer.dto.ts | 6 +- .../customers/services/customers.service.ts | 162 +++---- .../employees/dtos/create-employee.dto.ts | 208 ++++----- .../employees/dtos/list-employee.dto.ts | 16 +- .../employees/dtos/profil-employee.dto.ts | 26 +- .../employees/dtos/update-employee.dto.ts | 36 +- .../employees/services/employees.service.ts | 418 +++++++++--------- src/modules/employees/utils/employee.utils.ts | 18 +- 22 files changed, 819 insertions(+), 821 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 60503eb..fd3b522 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -14,48 +14,48 @@ datasource db { url = env("DATABASE_URL_DEV") } -model Users { - id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid - first_name String - last_name String - email String @unique - phone_number String @unique - residence String? - role Roles @default(GUEST) +// model Users { +// id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid +// first_name String +// last_name String +// email String @unique +// phone_number String @unique +// residence String? +// role Roles @default(GUEST) - employee Employees? @relation("UserEmployee") - oauth_sessions OAuthSessions[] @relation("UserOAuthSessions") - preferences Preferences? @relation("UserPreferences") +// employee Employees? @relation("UserEmployee") +// oauth_sessions OAuthSessions[] @relation("UserOAuthSessions") +// preferences Preferences? @relation("UserPreferences") - @@map("users") -} +// @@map("users") +// } -model Employees { - id Int @id @default(autoincrement()) - user Users @relation("UserEmployee", fields: [user_id], references: [id]) - user_id String @unique @db.Uuid - supervisor Employees? @relation("EmployeeSupervisor", fields: [supervisor_id], references: [id]) - supervisor_id Int? +// model Employees { +// id Int @id @default(autoincrement()) +// user Users @relation("UserEmployee", fields: [user_id], references: [id]) +// user_id String @unique @db.Uuid +// supervisor Employees? @relation("EmployeeSupervisor", fields: [supervisor_id], references: [id]) +// supervisor_id Int? - external_payroll_id Int - company_code Int - first_work_day DateTime @db.Date - last_work_day DateTime? @db.Date - job_title String? - is_supervisor Boolean @default(false) +// external_payroll_id Int +// company_code Int +// first_work_day DateTime @db.Date +// last_work_day DateTime? @db.Date +// job_title String? +// is_supervisor Boolean @default(false) - crew Employees[] @relation("EmployeeSupervisor") - timesheet Timesheets[] @relation("TimesheetEmployee") - leave_request LeaveRequests[] @relation("LeaveRequestEmployee") - schedule_presets SchedulePresets[] @relation("SchedulePreset") +// crew Employees[] @relation("EmployeeSupervisor") +// timesheet Timesheets[] @relation("TimesheetEmployee") +// leave_request LeaveRequests[] @relation("LeaveRequestEmployee") +// schedule_presets SchedulePresets[] @relation("SchedulePreset") - @@map("employees") -} +// @@map("employees") +// } model LeaveRequests { id Int @id @default(autoincrement()) - employee Employees @relation("LeaveRequestEmployee", fields: [employee_id], references: [id]) + // employee Employees @relation("LeaveRequestEmployee", fields: [employee_id], references: [id]) employee_id Int bank_code BankCodes @relation("LeaveRequestBankCodes", fields: [bank_code_id], references: [id]) bank_code_id Int @@ -107,7 +107,7 @@ view PayPeriods { model Timesheets { id Int @id @default(autoincrement()) - employee Employees @relation("TimesheetEmployee", fields: [employee_id], references: [id]) + // employee Employees @relation("TimesheetEmployee", fields: [employee_id], references: [id]) employee_id Int start_date DateTime @db.Date @@ -135,7 +135,7 @@ model TimesheetsArchive { model SchedulePresets { id Int @id @default(autoincrement()) - employee Employees @relation("SchedulePreset", fields: [employee_id], references: [id]) + // employee Employees @relation("SchedulePreset", fields: [employee_id], references: [id]) employee_id Int name String @@ -258,7 +258,7 @@ model ExpensesArchive { model OAuthSessions { id String @id @default(cuid()) - user Users @relation("UserOAuthSessions", fields: [user_id], references: [id]) + // user Users @relation("UserOAuthSessions", fields: [user_id], references: [id]) user_id String @db.Uuid application String access_token String @unique @@ -327,7 +327,7 @@ model AttachmentVariants { model Preferences { id Int @id @default(autoincrement()) - user Users @relation("UserPreferences", fields: [user_id], references: [id]) + // user Users @relation("UserPreferences", fields: [user_id], references: [id]) user_id String @unique @db.Uuid notifications Int @default(0) diff --git a/src/app.module.ts b/src/app.module.ts index 901f74e..4519929 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -3,7 +3,7 @@ import { AppController } from './app.controller'; import { AppService } from './app.service'; // import { ArchivalModule } from './modules/archival/archival.module'; import { AuthenticationModule } from './modules/authentication/auth.module'; -import { BankCodesModule } from './modules/bank-codes/bank-codes.module'; +// import { BankCodesModule } from './modules/bank-codes/bank-codes.module'; // import { CsvExportModule } from './modules/exports/csv-exports.module'; import { HealthModule } from './health/health.module'; import { HealthController } from './health/health.controller'; @@ -23,7 +23,7 @@ import { TimeAndAttendanceModule } from 'src/time-and-attendance/time-and-attend @Module({ imports: [ AuthenticationModule, - BankCodesModule, + // BankCodesModule, ConfigModule.forRoot({isGlobal: true}), // CsvExportModule, // CustomersModule, diff --git a/src/modules/archival/controllers/expenses-archive.controller.ts b/src/modules/archival/controllers/expenses-archive.controller.ts index f256b82..ecfd7b8 100644 --- a/src/modules/archival/controllers/expenses-archive.controller.ts +++ b/src/modules/archival/controllers/expenses-archive.controller.ts @@ -1,32 +1,32 @@ -import { UseGuards, Controller, Get, Param, ParseIntPipe, NotFoundException } from "@nestjs/common"; -import { ApiTags, ApiOperation, ApiResponse } from "@nestjs/swagger"; -import { ExpensesArchive,Roles as RoleEnum } from "@prisma/client"; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { ExpensesArchivalService } from "src/time-and-attendance/modules/expenses/services/expenses-archival.service"; +// import { UseGuards, Controller, Get, Param, ParseIntPipe, NotFoundException } from "@nestjs/common"; +// import { ApiTags, ApiOperation, ApiResponse } from "@nestjs/swagger"; +// import { ExpensesArchive,Roles as RoleEnum } from "@prisma/client"; +// import { RolesAllowed } from "src/common/decorators/roles.decorators"; +// import { ExpensesArchivalService } from "src/time-and-attendance/modules/expenses/services/expenses-archival.service"; -@ApiTags('Expense Archives') -// @UseGuards() -@Controller('archives/expenses') -export class ExpensesArchiveController { - constructor(private readonly expensesService: ExpensesArchivalService) {} +// @ApiTags('Expense Archives') +// // @UseGuards() +// @Controller('archives/expenses') +// export class ExpensesArchiveController { +// constructor(private readonly expensesService: ExpensesArchivalService) {} - @Get() - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) - @ApiOperation({ summary: 'List of archived expenses'}) - @ApiResponse({ status: 200, description: 'List of archived expenses', isArray: true }) - async findAllArchived(): Promise { - return this.expensesService.findAllArchived(); - } +// @Get() +// //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) +// @ApiOperation({ summary: 'List of archived expenses'}) +// @ApiResponse({ status: 200, description: 'List of archived expenses', isArray: true }) +// async findAllArchived(): Promise { +// return this.expensesService.findAllArchived(); +// } - @Get() - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) - @ApiOperation({ summary: 'Fetch expense in archives with its Id'}) - @ApiResponse({ status: 200, description: 'Archived expense found'}) - async findOneArchived(@Param('id', ParseIntPipe) id: number ): Promise { - try{ - return await this.expensesService.findOneArchived(id); - }catch { - throw new NotFoundException(`Archived expense #${id} not found`); - } - } -} \ No newline at end of file +// @Get() +// //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) +// @ApiOperation({ summary: 'Fetch expense in archives with its Id'}) +// @ApiResponse({ status: 200, description: 'Archived expense found'}) +// async findOneArchived(@Param('id', ParseIntPipe) id: number ): Promise { +// try{ +// return await this.expensesService.findOneArchived(id); +// }catch { +// throw new NotFoundException(`Archived expense #${id} not found`); +// } +// } +// } \ No newline at end of file diff --git a/src/modules/archival/controllers/leave-requests-archive.controller.ts b/src/modules/archival/controllers/leave-requests-archive.controller.ts index 1c5e4be..ec1b046 100644 --- a/src/modules/archival/controllers/leave-requests-archive.controller.ts +++ b/src/modules/archival/controllers/leave-requests-archive.controller.ts @@ -1,7 +1,7 @@ -import { Controller } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +// import { Controller } from '@nestjs/common'; +// import { ApiTags } from '@nestjs/swagger'; -@ApiTags('LeaveRequests Archives') -// @UseGuards() -@Controller('archives/leaveRequests') -export class LeaveRequestsArchiveController {} \ No newline at end of file +// @ApiTags('LeaveRequests Archives') +// // @UseGuards() +// @Controller('archives/leaveRequests') +// export class LeaveRequestsArchiveController {} \ No newline at end of file diff --git a/src/modules/archival/controllers/shifts-archive.controller.ts b/src/modules/archival/controllers/shifts-archive.controller.ts index 7c88288..f322fa1 100644 --- a/src/modules/archival/controllers/shifts-archive.controller.ts +++ b/src/modules/archival/controllers/shifts-archive.controller.ts @@ -1,32 +1,32 @@ -import { Get, Param, ParseIntPipe, NotFoundException, Controller, UseGuards } from "@nestjs/common"; -import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { ShiftsArchive, Roles as RoleEnum } from "@prisma/client"; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { ShiftsArchivalService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service"; +// import { Get, Param, ParseIntPipe, NotFoundException, Controller, UseGuards } from "@nestjs/common"; +// import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +// import { ShiftsArchive, Roles as RoleEnum } from "@prisma/client"; +// import { RolesAllowed } from "src/common/decorators/roles.decorators"; +// import { ShiftsArchivalService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service"; -@ApiTags('Shift Archives') -// @UseGuards() -@Controller('archives/shifts') -export class ShiftsArchiveController { - constructor(private readonly shiftsService: ShiftsArchivalService) {} +// @ApiTags('Shift Archives') +// // @UseGuards() +// @Controller('archives/shifts') +// export class ShiftsArchiveController { +// constructor(private readonly shiftsService: ShiftsArchivalService) {} - @Get() - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) - @ApiOperation({ summary: 'List of archived shifts'}) - @ApiResponse({ status: 200, description: 'List of archived shifts', isArray: true }) - async findAllArchived(): Promise { - return this.shiftsService.findAllArchived(); - } +// @Get() +// //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) +// @ApiOperation({ summary: 'List of archived shifts'}) +// @ApiResponse({ status: 200, description: 'List of archived shifts', isArray: true }) +// async findAllArchived(): Promise { +// return this.shiftsService.findAllArchived(); +// } - @Get() - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR,RoleEnum.SUPERVISOR) - @ApiOperation({ summary: 'Fetch shift in archives with its Id'}) - @ApiResponse({ status: 200, description: 'Archived shift found'}) - async findOneArchived(@Param('id', ParseIntPipe) id: number ): Promise { - try{ - return await this.shiftsService.findOneArchived(id); - }catch { - throw new NotFoundException(`Archived shift #${id} not found`); - } - } -} \ No newline at end of file +// @Get() +// //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR,RoleEnum.SUPERVISOR) +// @ApiOperation({ summary: 'Fetch shift in archives with its Id'}) +// @ApiResponse({ status: 200, description: 'Archived shift found'}) +// async findOneArchived(@Param('id', ParseIntPipe) id: number ): Promise { +// try{ +// return await this.shiftsService.findOneArchived(id); +// }catch { +// throw new NotFoundException(`Archived shift #${id} not found`); +// } +// } +// } \ No newline at end of file diff --git a/src/modules/archival/controllers/timesheets-archive.controller.ts b/src/modules/archival/controllers/timesheets-archive.controller.ts index 3a8f0a2..888dc3c 100644 --- a/src/modules/archival/controllers/timesheets-archive.controller.ts +++ b/src/modules/archival/controllers/timesheets-archive.controller.ts @@ -1,33 +1,33 @@ -import { Controller, Get, NotFoundException, Param, ParseIntPipe, UseGuards } from "@nestjs/common"; -import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { TimesheetsArchive, Roles as RoleEnum } from '@prisma/client'; -import { TimesheetArchiveService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service"; +// import { Controller, Get, NotFoundException, Param, ParseIntPipe, UseGuards } from "@nestjs/common"; +// import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +// import { RolesAllowed } from "src/common/decorators/roles.decorators"; +// import { TimesheetsArchive, Roles as RoleEnum } from '@prisma/client'; +// import { TimesheetArchiveService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service"; -@ApiTags('Timesheet Archives') -// @UseGuards() -@Controller('archives/timesheets') -export class TimesheetsArchiveController { - constructor(private readonly timesheetsService: TimesheetArchiveService) {} +// @ApiTags('Timesheet Archives') +// // @UseGuards() +// @Controller('archives/timesheets') +// export class TimesheetsArchiveController { +// constructor(private readonly timesheetsService: TimesheetArchiveService) {} - @Get() - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) - @ApiOperation({ summary: 'List of archived timesheets'}) - @ApiResponse({ status: 200, description: 'List of archived timesheets', isArray: true }) - async findAllArchived(): Promise { - return this.timesheetsService.findAllArchived(); - } +// @Get() +// //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) +// @ApiOperation({ summary: 'List of archived timesheets'}) +// @ApiResponse({ status: 200, description: 'List of archived timesheets', isArray: true }) +// async findAllArchived(): Promise { +// return this.timesheetsService.findAllArchived(); +// } - @Get() - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) - @ApiOperation({ summary: 'Fetch timesheet in archives with its Id'}) - @ApiResponse({ status: 200, description: 'Archived timesheet found'}) - async findOneArchived(@Param('id', ParseIntPipe) id: number ): Promise { - try{ - return await this.timesheetsService.findOneArchived(id); - }catch { - throw new NotFoundException(`Archived timesheet #${id} not found`); - } - } +// @Get() +// //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) +// @ApiOperation({ summary: 'Fetch timesheet in archives with its Id'}) +// @ApiResponse({ status: 200, description: 'Archived timesheet found'}) +// async findOneArchived(@Param('id', ParseIntPipe) id: number ): Promise { +// try{ +// return await this.timesheetsService.findOneArchived(id); +// }catch { +// throw new NotFoundException(`Archived timesheet #${id} not found`); +// } +// } -} \ No newline at end of file +// } \ No newline at end of file diff --git a/src/modules/archival/services/archival.service.ts b/src/modules/archival/services/archival.service.ts index ded45eb..bed4191 100644 --- a/src/modules/archival/services/archival.service.ts +++ b/src/modules/archival/services/archival.service.ts @@ -1,38 +1,38 @@ -import { Injectable, Logger } from "@nestjs/common"; -import { Cron } from "@nestjs/schedule"; -import { ExpensesArchivalService } from "src/time-and-attendance/modules/expenses/services/expenses-archival.service"; -import { ShiftsArchivalService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service"; -import { TimesheetArchiveService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service"; +// import { Injectable, Logger } from "@nestjs/common"; +// import { Cron } from "@nestjs/schedule"; +// import { ExpensesArchivalService } from "src/time-and-attendance/modules/expenses/services/expenses-archival.service"; +// import { ShiftsArchivalService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service"; +// import { TimesheetArchiveService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service"; -@Injectable() -export class ArchivalService { - private readonly logger = new Logger(ArchivalService.name); +// @Injectable() +// export class ArchivalService { +// private readonly logger = new Logger(ArchivalService.name); - constructor( - private readonly timesheetsService: TimesheetArchiveService, - private readonly expensesService: ExpensesArchivalService, - private readonly shiftsService: ShiftsArchivalService, - ) {} +// constructor( +// private readonly timesheetsService: TimesheetArchiveService, +// private readonly expensesService: ExpensesArchivalService, +// private readonly shiftsService: ShiftsArchivalService, +// ) {} - @Cron('0 0 3 * * 1', {timeZone:'America/Toronto'}) // chaque premier lundi du mois à 03h00 - async handleMonthlyArchival() { - const today = new Date(); - const dayOfMonth = today.getDate(); +// @Cron('0 0 3 * * 1', {timeZone:'America/Toronto'}) // chaque premier lundi du mois à 03h00 +// async handleMonthlyArchival() { +// const today = new Date(); +// const dayOfMonth = today.getDate(); - if (dayOfMonth > 7) { - this.logger.warn('Archive {awaiting 1st monday of the month for archivation process}') - return; - } +// if (dayOfMonth > 7) { +// this.logger.warn('Archive {awaiting 1st monday of the month for archivation process}') +// return; +// } - this.logger.log('monthly archivation in process'); - try { - await this.timesheetsService.archiveOld(); - await this.expensesService.archiveOld(); - await this.shiftsService.archiveOld(); - // await this.leaveRequestsService.archiveExpired(); - this.logger.log('archivation process done'); - } catch (err) { - this.logger.error('an error occured during archivation process ', err); - } - } -} \ No newline at end of file +// this.logger.log('monthly archivation in process'); +// try { +// await this.timesheetsService.archiveOld(); +// await this.expensesService.archiveOld(); +// await this.shiftsService.archiveOld(); +// // await this.leaveRequestsService.archiveExpired(); +// this.logger.log('archivation process done'); +// } catch (err) { +// this.logger.error('an error occured during archivation process ', err); +// } +// } +// } \ No newline at end of file diff --git a/src/modules/attachments/controllers/attachments.controller.ts b/src/modules/attachments/controllers/attachments.controller.ts index 3a75d6a..4999771 100644 --- a/src/modules/attachments/controllers/attachments.controller.ts +++ b/src/modules/attachments/controllers/attachments.controller.ts @@ -4,9 +4,7 @@ import { Controller,NotFoundException, UseInterceptors, Post, Get, Param, Res, UploadedFile, BadRequestException, UnsupportedMediaTypeException, Body, Delete, Query, - DefaultValuePipe, - ParseIntPipe - } from "@nestjs/common"; +} from "@nestjs/common"; import { maxUploadBytes, allowedMimes } from "../config/upload.config"; import { memoryStorage } from 'multer'; import { fileTypeFromBuffer, fileTypeFromFile } from "file-type"; diff --git a/src/modules/bank-codes/bank-codes.module.ts b/src/modules/bank-codes/bank-codes.module.ts index 80ec6e3..816c440 100644 --- a/src/modules/bank-codes/bank-codes.module.ts +++ b/src/modules/bank-codes/bank-codes.module.ts @@ -1,11 +1,11 @@ -import { Module } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; -import { BankCodesControllers } from "./controllers/bank-codes.controller"; -import { BankCodesService } from "./services/bank-codes.service"; +// import { Module } from "@nestjs/common"; +// import { PrismaService } from "src/prisma/prisma.service"; +// import { BankCodesControllers } from "./controllers/bank-codes.controller"; +// import { BankCodesService } from "./services/bank-codes.service"; -@Module({ - controllers: [BankCodesControllers], - providers: [BankCodesService, PrismaService], -}) +// @Module({ +// controllers: [BankCodesControllers], +// providers: [BankCodesService, PrismaService], +// }) -export class BankCodesModule {} \ No newline at end of file +// export class BankCodesModule {} \ No newline at end of file diff --git a/src/modules/bank-codes/controllers/bank-codes.controller.ts b/src/modules/bank-codes/controllers/bank-codes.controller.ts index 678336c..4588bbf 100644 --- a/src/modules/bank-codes/controllers/bank-codes.controller.ts +++ b/src/modules/bank-codes/controllers/bank-codes.controller.ts @@ -1,49 +1,49 @@ -import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; -import { BankCodesService } from "../services/bank-codes.service"; -import { CreateBankCodeDto } from "../dtos/create-bank-code.dto"; -import { UpdateBankCodeDto } from "../dtos/update-bank-code.dto"; -import { ApiBadRequestResponse, ApiNotFoundResponse, ApiOperation, ApiResponse } from "@nestjs/swagger"; +// import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; +// import { BankCodesService } from "../services/bank-codes.service"; +// import { CreateBankCodeDto } from "../dtos/create-bank-code.dto"; +// import { UpdateBankCodeDto } from "../dtos/update-bank-code.dto"; +// import { ApiBadRequestResponse, ApiNotFoundResponse, ApiOperation, ApiResponse } from "@nestjs/swagger"; -@Controller('bank-codes') -export class BankCodesControllers { - constructor(private readonly bankCodesService: BankCodesService) {} - //_____________________________________________________________________________________________ - // Deprecated or unused methods - //_____________________________________________________________________________________________ +// @Controller('bank-codes') +// export class BankCodesControllers { +// constructor(private readonly bankCodesService: BankCodesService) {} +// //_____________________________________________________________________________________________ +// // Deprecated or unused methods +// //_____________________________________________________________________________________________ - // @Post() - // @ApiOperation({ summary: 'Create a new bank code' }) - // @ApiResponse({ status: 201, description: 'Bank code successfully created.' }) - // @ApiBadRequestResponse({ description: 'Invalid input data.' }) - // create(@Body() dto: CreateBankCodeDto) { - // return this.bankCodesService.create(dto); - // } +// // @Post() +// // @ApiOperation({ summary: 'Create a new bank code' }) +// // @ApiResponse({ status: 201, description: 'Bank code successfully created.' }) +// // @ApiBadRequestResponse({ description: 'Invalid input data.' }) +// // create(@Body() dto: CreateBankCodeDto) { +// // return this.bankCodesService.create(dto); +// // } - // @Get() - // @ApiOperation({ summary: 'Retrieve all bank codes' }) - // @ApiResponse({ status: 200, description: 'List of bank codes.' }) - // findAll() { - // return this.bankCodesService.findAll(); - // } +// // @Get() +// // @ApiOperation({ summary: 'Retrieve all bank codes' }) +// // @ApiResponse({ status: 200, description: 'List of bank codes.' }) +// // findAll() { +// // return this.bankCodesService.findAll(); +// // } - // @Get(':id') - // @ApiOperation({ summary: 'Retrieve a bank code by its ID' }) - // @ApiNotFoundResponse({ description: 'Bank code not found.' }) - // findOne(@Param('id', ParseIntPipe) id: number){ - // return this.bankCodesService.findOne(id); - // } +// // @Get(':id') +// // @ApiOperation({ summary: 'Retrieve a bank code by its ID' }) +// // @ApiNotFoundResponse({ description: 'Bank code not found.' }) +// // findOne(@Param('id', ParseIntPipe) id: number){ +// // return this.bankCodesService.findOne(id); +// // } - // @Patch(':id') - // @ApiOperation({ summary: 'Update an existing bank code' }) - // @ApiNotFoundResponse({ description: 'Bank code not found.' }) - // update(@Param('id', ParseIntPipe) id: number, @Body() dto: UpdateBankCodeDto) { - // return this.bankCodesService.update(id, dto) - // } +// // @Patch(':id') +// // @ApiOperation({ summary: 'Update an existing bank code' }) +// // @ApiNotFoundResponse({ description: 'Bank code not found.' }) +// // update(@Param('id', ParseIntPipe) id: number, @Body() dto: UpdateBankCodeDto) { +// // return this.bankCodesService.update(id, dto) +// // } - // @Delete(':id') - // @ApiOperation({ summary: 'Delete a bank code' }) - // @ApiNotFoundResponse({ description: 'Bank code not found.' }) - // remove(@Param('id', ParseIntPipe) id: number) { - // return this.bankCodesService.remove(id); - // } -} \ No newline at end of file +// // @Delete(':id') +// // @ApiOperation({ summary: 'Delete a bank code' }) +// // @ApiNotFoundResponse({ description: 'Bank code not found.' }) +// // remove(@Param('id', ParseIntPipe) id: number) { +// // return this.bankCodesService.remove(id); +// // } +// } \ No newline at end of file diff --git a/src/modules/bank-codes/dtos/create-bank-code.dto.ts b/src/modules/bank-codes/dtos/create-bank-code.dto.ts index f2bec7b..a08020a 100644 --- a/src/modules/bank-codes/dtos/create-bank-code.dto.ts +++ b/src/modules/bank-codes/dtos/create-bank-code.dto.ts @@ -1,46 +1,46 @@ -import { ApiProperty } from "@nestjs/swagger"; -import { Type } from "class-transformer"; -import { Allow, IsNotEmpty, IsNumber, IsString } from "class-validator"; +// import { ApiProperty } from "@nestjs/swagger"; +// import { Type } from "class-transformer"; +// import { Allow, IsNotEmpty, IsNumber, IsString } from "class-validator"; -export class CreateBankCodeDto { - @ApiProperty({ - example: 1, - description: 'Unique ID of a bank-code (auto-generated)', - readOnly: true, - }) - @Allow() - id: number; +// export class CreateBankCodeDto { +// @ApiProperty({ +// example: 1, +// description: 'Unique ID of a bank-code (auto-generated)', +// readOnly: true, +// }) +// @Allow() +// id: number; - @ApiProperty({ - example: 'regular, vacation, emergency, sick, parental, etc', - description: 'Type of codes', - }) - @IsString() - @IsNotEmpty() - type: string; +// @ApiProperty({ +// example: 'regular, vacation, emergency, sick, parental, etc', +// description: 'Type of codes', +// }) +// @IsString() +// @IsNotEmpty() +// type: string; - @ApiProperty({ - example: 'shift, expense, leave', - description: 'categorie of the related code', - }) - @IsString() - @IsNotEmpty() - categorie: string; +// @ApiProperty({ +// example: 'shift, expense, leave', +// description: 'categorie of the related code', +// }) +// @IsString() +// @IsNotEmpty() +// categorie: string; - @ApiProperty({ - example: '0, 0.72, 1, 1.5, 2', - description: 'modifier number to apply to salary', - }) - @Type(()=> Number) - @IsNumber() - @IsNotEmpty() - modifier: number; +// @ApiProperty({ +// example: '0, 0.72, 1, 1.5, 2', +// description: 'modifier number to apply to salary', +// }) +// @Type(()=> Number) +// @IsNumber() +// @IsNotEmpty() +// modifier: number; - @ApiProperty({ - example: 'G1, G345, G501, G43, G700', - description: 'codes given by the bank', - }) - @IsString() - @IsNotEmpty() - bank_code: string; -} \ No newline at end of file +// @ApiProperty({ +// example: 'G1, G345, G501, G43, G700', +// description: 'codes given by the bank', +// }) +// @IsString() +// @IsNotEmpty() +// bank_code: string; +// } \ No newline at end of file diff --git a/src/modules/bank-codes/dtos/update-bank-code.dto.ts b/src/modules/bank-codes/dtos/update-bank-code.dto.ts index 4033484..884e544 100644 --- a/src/modules/bank-codes/dtos/update-bank-code.dto.ts +++ b/src/modules/bank-codes/dtos/update-bank-code.dto.ts @@ -1,4 +1,4 @@ -import { PartialType } from "@nestjs/swagger"; -import { CreateBankCodeDto } from "./create-bank-code.dto"; +// import { PartialType } from "@nestjs/swagger"; +// import { CreateBankCodeDto } from "./create-bank-code.dto"; -export class UpdateBankCodeDto extends PartialType(CreateBankCodeDto) {} \ No newline at end of file +// export class UpdateBankCodeDto extends PartialType(CreateBankCodeDto) {} \ No newline at end of file diff --git a/src/modules/bank-codes/services/bank-codes.service.ts b/src/modules/bank-codes/services/bank-codes.service.ts index 5e1549a..fbb8745 100644 --- a/src/modules/bank-codes/services/bank-codes.service.ts +++ b/src/modules/bank-codes/services/bank-codes.service.ts @@ -1,51 +1,51 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; -import { CreateBankCodeDto } from "../dtos/create-bank-code.dto"; -import { BankCodes } from "@prisma/client"; -import { UpdateBankCodeDto } from "../dtos/update-bank-code.dto"; +// import { Injectable, NotFoundException } from "@nestjs/common"; +// import { PrismaService } from "src/prisma/prisma.service"; +// import { CreateBankCodeDto } from "../dtos/create-bank-code.dto"; +// import { BankCodes } from "@prisma/client"; +// import { UpdateBankCodeDto } from "../dtos/update-bank-code.dto"; -@Injectable() -export class BankCodesService { - constructor(private readonly prisma: PrismaService) {} +// @Injectable() +// export class BankCodesService { +// constructor(private readonly prisma: PrismaService) {} - async create(dto: CreateBankCodeDto): Promise{ - return this.prisma.bankCodes.create({ - data: { - type: dto.type, - categorie: dto.categorie, - modifier: dto.modifier, - bank_code: dto.bank_code, - }, - }); - } +// async create(dto: CreateBankCodeDto): Promise{ +// return this.prisma.bankCodes.create({ +// data: { +// type: dto.type, +// categorie: dto.categorie, +// modifier: dto.modifier, +// bank_code: dto.bank_code, +// }, +// }); +// } - findAll() { - return this.prisma.bankCodes.findMany(); - } +// findAll() { +// return this.prisma.bankCodes.findMany(); +// } - async findOne(id: number) { - const bankCode = await this.prisma.bankCodes.findUnique({ where: {id} }); +// async findOne(id: number) { +// const bankCode = await this.prisma.bankCodes.findUnique({ where: {id} }); - if(!bankCode) throw new NotFoundException(`Bank Code #${id} not found`); +// if(!bankCode) throw new NotFoundException(`Bank Code #${id} not found`); - return bankCode; - } +// return bankCode; +// } - async update(id:number, dto: UpdateBankCodeDto) { - return await this.prisma.bankCodes.update({ - where: { id }, - data: { - type: dto.type, - categorie: dto.categorie, - modifier: dto.modifier as any, - bank_code: dto.bank_code, - }, - }); - } +// async update(id:number, dto: UpdateBankCodeDto) { +// return await this.prisma.bankCodes.update({ +// where: { id }, +// data: { +// type: dto.type, +// categorie: dto.categorie, +// modifier: dto.modifier as any, +// bank_code: dto.bank_code, +// }, +// }); +// } - async remove(id: number) { - await this.findOne(id); - return this.prisma.bankCodes.delete({ where: {id} }); - } +// async remove(id: number) { +// await this.findOne(id); +// return this.prisma.bankCodes.delete({ where: {id} }); +// } -} \ No newline at end of file +// } \ No newline at end of file diff --git a/src/modules/customers/dtos/create-customer.dto.ts b/src/modules/customers/dtos/create-customer.dto.ts index 8382f20..8a136ac 100644 --- a/src/modules/customers/dtos/create-customer.dto.ts +++ b/src/modules/customers/dtos/create-customer.dto.ts @@ -1,79 +1,79 @@ -import { ApiProperty } from "@nestjs/swagger"; -import { Type } from "class-transformer"; -import { - Allow, - IsEmail, - IsInt, - IsNotEmpty, - IsOptional, - IsPositive, - IsString, - IsUUID, -} from "class-validator"; +// import { ApiProperty } from "@nestjs/swagger"; +// import { Type } from "class-transformer"; +// import { +// Allow, +// IsEmail, +// IsInt, +// IsNotEmpty, +// IsOptional, +// IsPositive, +// IsString, +// IsUUID, +// } from "class-validator"; -export class CreateCustomerDto { - @ApiProperty({ - example: 1, - description: 'Unique ID of a customer(primary-key, auto-incremented)', - }) - @Allow() - id?: number; +// export class CreateCustomerDto { +// @ApiProperty({ +// example: 1, +// description: 'Unique ID of a customer(primary-key, auto-incremented)', +// }) +// @Allow() +// id?: number; - @ApiProperty({ - example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d', - description: 'UUID of the user linked to that customer', - }) - @IsUUID() - @IsOptional() - user_id?: string; +// @ApiProperty({ +// example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d', +// description: 'UUID of the user linked to that customer', +// }) +// @IsUUID() +// @IsOptional() +// user_id?: string; - @ApiProperty({ - example: 'Gandalf', - description: 'Customer`s first name', - }) - @IsString() - @IsNotEmpty() - first_name: string; +// @ApiProperty({ +// example: 'Gandalf', +// description: 'Customer`s first name', +// }) +// @IsString() +// @IsNotEmpty() +// first_name: string; - @ApiProperty({ - example: 'TheGray', - description: 'Customer`s last name', - }) - @IsString() - @IsNotEmpty() - last_name: string; +// @ApiProperty({ +// example: 'TheGray', +// description: 'Customer`s last name', +// }) +// @IsString() +// @IsNotEmpty() +// last_name: string; - @ApiProperty({ - example: 'you_shall_not_pass@middleEarth.com', - description: 'Customer`s email', - }) - @IsEmail() - @IsOptional() - email: string; +// @ApiProperty({ +// example: 'you_shall_not_pass@middleEarth.com', +// description: 'Customer`s email', +// }) +// @IsEmail() +// @IsOptional() +// email: string; - @ApiProperty({ - example: '8436637464', - description: 'Customer`s phone number', - }) - @IsString() - phone_number: string; +// @ApiProperty({ +// example: '8436637464', +// description: 'Customer`s phone number', +// }) +// @IsString() +// phone_number: string; - @ApiProperty({ - example: '1 Ringbearer`s way, Mount Doom city, ME, T1R 1N6 ', - description: 'Customer`s residence', - required: false, - }) - @IsString() - @IsOptional() - residence?: string; +// @ApiProperty({ +// example: '1 Ringbearer`s way, Mount Doom city, ME, T1R 1N6 ', +// description: 'Customer`s residence', +// required: false, +// }) +// @IsString() +// @IsOptional() +// residence?: string; - @ApiProperty({ - example: '4263253', - description: 'Customer`s invoice number', - required: false, - }) - @Type(() => Number) - @IsInt() - @IsNotEmpty() - invoice_id: number; -} \ No newline at end of file +// @ApiProperty({ +// example: '4263253', +// description: 'Customer`s invoice number', +// required: false, +// }) +// @Type(() => Number) +// @IsInt() +// @IsNotEmpty() +// invoice_id: number; +// } \ No newline at end of file diff --git a/src/modules/customers/dtos/update-customer.dto.ts b/src/modules/customers/dtos/update-customer.dto.ts index fc1ba39..2f52413 100644 --- a/src/modules/customers/dtos/update-customer.dto.ts +++ b/src/modules/customers/dtos/update-customer.dto.ts @@ -1,4 +1,4 @@ -import { PartialType } from "@nestjs/swagger"; -import { CreateCustomerDto } from "./create-customer.dto"; +// import { PartialType } from "@nestjs/swagger"; +// import { CreateCustomerDto } from "./create-customer.dto"; -export class UpdateCustomerDto extends PartialType(CreateCustomerDto) {} \ No newline at end of file +// export class UpdateCustomerDto extends PartialType(CreateCustomerDto) {} \ No newline at end of file diff --git a/src/modules/customers/services/customers.service.ts b/src/modules/customers/services/customers.service.ts index b0b68c8..2662250 100644 --- a/src/modules/customers/services/customers.service.ts +++ b/src/modules/customers/services/customers.service.ts @@ -1,93 +1,93 @@ -import { Injectable } from '@nestjs/common'; +// import { Injectable } from '@nestjs/common'; -@Injectable() -export class CustomersService { +// @Injectable() +// export class CustomersService { -//_____________________________________________________________________________________________ -// Deprecated or unused methods -//_____________________________________________________________________________________________ +// //_____________________________________________________________________________________________ +// // Deprecated or unused methods +// //_____________________________________________________________________________________________ -// constructor(private readonly prisma: PrismaService) {} +// // constructor(private readonly prisma: PrismaService) {} -// async create(dto: CreateCustomerDto): Promise { -// const { -// first_name, -// last_name, -// email, -// phone_number, -// residence, -// invoice_id, -// } = dto; +// // async create(dto: CreateCustomerDto): Promise { +// // const { +// // first_name, +// // last_name, +// // email, +// // phone_number, +// // residence, +// // invoice_id, +// // } = dto; -// return this.prisma.$transaction(async (transaction) => { -// const user: Users = await transaction.users.create({ -// data: { -// first_name, -// last_name, -// email, -// phone_number, -// residence, -// }, -// }); -// return transaction.customers.create({ -// data: { -// user_id: user.id, -// invoice_id, -// }, -// }); -// }); -// } +// // return this.prisma.$transaction(async (transaction) => { +// // const user: Users = await transaction.users.create({ +// // data: { +// // first_name, +// // last_name, +// // email, +// // phone_number, +// // residence, +// // }, +// // }); +// // return transaction.customers.create({ +// // data: { +// // user_id: user.id, +// // invoice_id, +// // }, +// // }); +// // }); +// // } -// findAll(): Promise { -// return this.prisma.customers.findMany({ -// include: { user: true }, -// }) -// } +// // findAll(): Promise { +// // return this.prisma.customers.findMany({ +// // include: { user: true }, +// // }) +// // } -// async findOne(id:number): Promise { -// const customer = await this.prisma.customers.findUnique({ -// where: { id }, -// include: { user: true }, -// }); -// if(!customer) throw new NotFoundException(`Customer #${id} not found`); -// return customer; -// } +// // async findOne(id:number): Promise { +// // const customer = await this.prisma.customers.findUnique({ +// // where: { id }, +// // include: { user: true }, +// // }); +// // if(!customer) throw new NotFoundException(`Customer #${id} not found`); +// // return customer; +// // } -// async update(id: number,dto: UpdateCustomerDto): Promise { -// const customer = await this.findOne(id); +// // async update(id: number,dto: UpdateCustomerDto): Promise { +// // const customer = await this.findOne(id); -// const { -// first_name, -// last_name, -// email, -// phone_number, -// residence, -// invoice_id, -// } = dto; +// // const { +// // first_name, +// // last_name, +// // email, +// // phone_number, +// // residence, +// // invoice_id, +// // } = dto; -// return this.prisma.$transaction(async (transaction) => { -// await transaction.users.update({ -// where: { id: customer.user_id }, -// data: { -// ...(first_name !== undefined && { first_name }), -// ...(last_name !== undefined && { last_name }), -// ...(email !== undefined && { email }), -// ...(phone_number !== undefined && { phone_number }), -// ...(residence !== undefined && { residence }), -// }, -// }); +// // return this.prisma.$transaction(async (transaction) => { +// // await transaction.users.update({ +// // where: { id: customer.user_id }, +// // data: { +// // ...(first_name !== undefined && { first_name }), +// // ...(last_name !== undefined && { last_name }), +// // ...(email !== undefined && { email }), +// // ...(phone_number !== undefined && { phone_number }), +// // ...(residence !== undefined && { residence }), +// // }, +// // }); -// return transaction.customers.update({ -// where: { id }, -// data: { -// ...(invoice_id !== undefined && { invoice_id }), -// }, -// }); -// }); +// // return transaction.customers.update({ +// // where: { id }, +// // data: { +// // ...(invoice_id !== undefined && { invoice_id }), +// // }, +// // }); +// // }); +// // } + +// // async remove(id: number): Promise { +// // await this.findOne(id); +// // return this.prisma.customers.delete({ where: { id }}); +// // } // } - -// async remove(id: number): Promise { -// await this.findOne(id); -// return this.prisma.customers.delete({ where: { id }}); -// } -} diff --git a/src/modules/employees/dtos/create-employee.dto.ts b/src/modules/employees/dtos/create-employee.dto.ts index 89279ef..4fbbaaa 100644 --- a/src/modules/employees/dtos/create-employee.dto.ts +++ b/src/modules/employees/dtos/create-employee.dto.ts @@ -1,118 +1,118 @@ -import { - Allow, - IsBoolean, - IsDateString, - IsEmail, - IsInt, - IsNotEmpty, - IsOptional, - IsPositive, - IsString, - IsUUID, -} from 'class-validator'; -import { Type } from 'class-transformer'; -import { ApiProperty } from '@nestjs/swagger'; +// import { +// Allow, +// IsBoolean, +// IsDateString, +// IsEmail, +// IsInt, +// IsNotEmpty, +// IsOptional, +// IsPositive, +// IsString, +// IsUUID, +// } from 'class-validator'; +// import { Type } from 'class-transformer'; +// import { ApiProperty } from '@nestjs/swagger'; -export class CreateEmployeeDto { - @ApiProperty({ - example: 1, - description: 'Unique ID of an employee(primary-key, auto-incremented)', - }) - @Allow() - id: number; +// export class CreateEmployeeDto { +// @ApiProperty({ +// example: 1, +// description: 'Unique ID of an employee(primary-key, auto-incremented)', +// }) +// @Allow() +// id: number; - @ApiProperty({ - example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d', - description: 'UUID of the user linked to that employee', - }) - @IsUUID() - @IsOptional() - user_id?: string; +// @ApiProperty({ +// example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d', +// description: 'UUID of the user linked to that employee', +// }) +// @IsUUID() +// @IsOptional() +// user_id?: string; - @ApiProperty({ - example: 'Frodo', - description: 'Employee`s first name', - }) - @IsString() - @IsNotEmpty() - first_name: string; +// @ApiProperty({ +// example: 'Frodo', +// description: 'Employee`s first name', +// }) +// @IsString() +// @IsNotEmpty() +// first_name: string; - @ApiProperty({ - example: 'Baggins', - description: 'Employee`s last name', - }) - @IsString() - @IsNotEmpty() - last_name: string; +// @ApiProperty({ +// example: 'Baggins', +// description: 'Employee`s last name', +// }) +// @IsString() +// @IsNotEmpty() +// last_name: string; - @ApiProperty({ - example: 'i_cant_do_this_sam@targointernet.com', - description: 'Employee`s email', - }) - @IsEmail() - @IsOptional() - email: string; +// @ApiProperty({ +// example: 'i_cant_do_this_sam@targointernet.com', +// description: 'Employee`s email', +// }) +// @IsEmail() +// @IsOptional() +// email: string; - @IsOptional() - @IsBoolean() - is_supervisor: boolean; +// @IsOptional() +// @IsBoolean() +// is_supervisor: boolean; - @ApiProperty({ - example: '82538437464', - description: 'Employee`s phone number', - }) - @IsString() - phone_number: string; +// @ApiProperty({ +// example: '82538437464', +// description: 'Employee`s phone number', +// }) +// @IsString() +// phone_number: string; - @ApiProperty({ - example: '1 Bagshot Row, Hobbiton, The Shire, Middle-earth', - description: 'Employee`s residence', - required: false, - }) - @IsString() - @IsOptional() - residence?: string; +// @ApiProperty({ +// example: '1 Bagshot Row, Hobbiton, The Shire, Middle-earth', +// description: 'Employee`s residence', +// required: false, +// }) +// @IsString() +// @IsOptional() +// residence?: string; - @ApiProperty({ - example: 7464, - description: 'external ID for the pay system', - }) - @IsInt() - @IsPositive() - @Type(() => Number) - external_payroll_id: number; +// @ApiProperty({ +// example: 7464, +// description: 'external ID for the pay system', +// }) +// @IsInt() +// @IsPositive() +// @Type(() => Number) +// external_payroll_id: number; - @ApiProperty({ - example: 335567447, - description: 'Employee`s company code', - }) - @IsInt() - @IsPositive() - @Type(() => Number) - company_code: number; +// @ApiProperty({ +// example: 335567447, +// description: 'Employee`s company code', +// }) +// @IsInt() +// @IsPositive() +// @Type(() => Number) +// company_code: number; - @ApiProperty({ - example:'technicient', - description: 'employee`s job title', - }) - @IsString() - @IsOptional() - job_title: string; +// @ApiProperty({ +// example:'technicient', +// description: 'employee`s job title', +// }) +// @IsString() +// @IsOptional() +// job_title: string; - @ApiProperty({ - example: '23/09/3018', - description: 'Employee`s first working day', - }) - @IsDateString() - first_work_day: string; +// @ApiProperty({ +// example: '23/09/3018', +// description: 'Employee`s first working day', +// }) +// @IsDateString() +// first_work_day: string; - @ApiProperty({ - example: '25/03/3019', - description: 'Employee`s last working day', - required: false, - }) - @IsDateString() - @IsOptional() - last_work_day?: string; -} +// @ApiProperty({ +// example: '25/03/3019', +// description: 'Employee`s last working day', +// required: false, +// }) +// @IsDateString() +// @IsOptional() +// last_work_day?: string; +// } diff --git a/src/modules/employees/dtos/list-employee.dto.ts b/src/modules/employees/dtos/list-employee.dto.ts index 39abf03..6adbe3f 100644 --- a/src/modules/employees/dtos/list-employee.dto.ts +++ b/src/modules/employees/dtos/list-employee.dto.ts @@ -1,8 +1,8 @@ -export class EmployeeListItemDto { - first_name: string; - last_name: string; - email: string; - supervisor_full_name: string | null; - company_name: number | null; - job_title: string | null; -} \ No newline at end of file +// export class EmployeeListItemDto { +// first_name: string; +// last_name: string; +// email: string; +// supervisor_full_name: string | null; +// company_name: number | null; +// job_title: string | null; +// } \ No newline at end of file diff --git a/src/modules/employees/dtos/profil-employee.dto.ts b/src/modules/employees/dtos/profil-employee.dto.ts index c6836cf..adbf38e 100644 --- a/src/modules/employees/dtos/profil-employee.dto.ts +++ b/src/modules/employees/dtos/profil-employee.dto.ts @@ -1,13 +1,13 @@ -export class EmployeeProfileItemDto { - first_name: string; - last_name: string; - employee_full_name: string; - supervisor_full_name: string | null; - company_name: number | null; - job_title: string | null; - email: string | null; - phone_number: string; - first_work_day: string; - last_work_day?: string | null; - residence: string | null; -} \ No newline at end of file +// export class EmployeeProfileItemDto { +// first_name: string; +// last_name: string; +// employee_full_name: string; +// supervisor_full_name: string | null; +// company_name: number | null; +// job_title: string | null; +// email: string | null; +// phone_number: string; +// first_work_day: string; +// last_work_day?: string | null; +// residence: string | null; +// } \ No newline at end of file diff --git a/src/modules/employees/dtos/update-employee.dto.ts b/src/modules/employees/dtos/update-employee.dto.ts index 334a01a..1efbbfd 100644 --- a/src/modules/employees/dtos/update-employee.dto.ts +++ b/src/modules/employees/dtos/update-employee.dto.ts @@ -1,22 +1,22 @@ -import { ApiProperty, PartialType } from '@nestjs/swagger'; -import { CreateEmployeeDto } from './create-employee.dto'; -import { IsDateString, IsOptional, Max } from 'class-validator'; +// import { ApiProperty, PartialType } from '@nestjs/swagger'; +// import { CreateEmployeeDto } from './create-employee.dto'; +// import { IsDateString, IsOptional, Max } from 'class-validator'; -export class UpdateEmployeeDto extends PartialType(CreateEmployeeDto) { - @ApiProperty({ required: false, type: Date, description: 'New hire date or undefined' }) - @IsDateString() - @IsOptional() - first_work_day?: string; +// export class UpdateEmployeeDto extends PartialType(CreateEmployeeDto) { +// @ApiProperty({ required: false, type: Date, description: 'New hire date or undefined' }) +// @IsDateString() +// @IsOptional() +// first_work_day?: string; - @ApiProperty({ required: false, type: Date, description: 'Termination date (null to restore)' }) - @IsDateString() - @IsOptional() - last_work_day?: string; +// @ApiProperty({ required: false, type: Date, description: 'Termination date (null to restore)' }) +// @IsDateString() +// @IsOptional() +// last_work_day?: string; - @ApiProperty({ required: false, type: Number, description: 'Supervisor ID' }) - @IsOptional() - supervisor_id?: number; +// @ApiProperty({ required: false, type: Number, description: 'Supervisor ID' }) +// @IsOptional() +// supervisor_id?: number; - @IsOptional() - phone_number: string; -} +// @IsOptional() +// phone_number: string; +// } diff --git a/src/modules/employees/services/employees.service.ts b/src/modules/employees/services/employees.service.ts index 3627476..b44bf7d 100644 --- a/src/modules/employees/services/employees.service.ts +++ b/src/modules/employees/services/employees.service.ts @@ -1,230 +1,230 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { EmployeeListItemDto } from '../dtos/list-employee.dto'; -import { EmployeeProfileItemDto } from '../dtos/profil-employee.dto'; +// import { Injectable, NotFoundException } from '@nestjs/common'; +// import { PrismaService } from 'src/prisma/prisma.service'; +// import { EmployeeListItemDto } from '../dtos/list-employee.dto'; +// import { EmployeeProfileItemDto } from '../dtos/profil-employee.dto'; -@Injectable() -export class EmployeesService { - constructor(private readonly prisma: PrismaService) { } +// @Injectable() +// export class EmployeesService { +// constructor(private readonly prisma: PrismaService) { } - findListEmployees(): Promise { - return this.prisma.employees.findMany({ - select: { - user: { - select: { - first_name: true, - last_name: true, - email: true, - }, - }, - supervisor: { - select: { - user: { - select: { - first_name: true, - last_name: true, - }, - }, - }, - }, - job_title: true, - company_code: true, - } - }).then(rows => rows.map(r => ({ - first_name: r.user.first_name, - last_name: r.user.last_name, - email: r.user.email, - company_name: r.company_code, - job_title: r.job_title, - employee_full_name: `${r.user.first_name} ${r.user.last_name}`, - supervisor_full_name: r.supervisor ? `${r.supervisor.user.first_name} ${r.supervisor.user.last_name}` : null, - })), - ); - } +// findListEmployees(): Promise { +// return this.prisma.employees.findMany({ +// select: { +// user: { +// select: { +// first_name: true, +// last_name: true, +// email: true, +// }, +// }, +// supervisor: { +// select: { +// user: { +// select: { +// first_name: true, +// last_name: true, +// }, +// }, +// }, +// }, +// job_title: true, +// company_code: true, +// } +// }).then(rows => rows.map(r => ({ +// first_name: r.user.first_name, +// last_name: r.user.last_name, +// email: r.user.email, +// company_name: r.company_code, +// job_title: r.job_title, +// employee_full_name: `${r.user.first_name} ${r.user.last_name}`, +// supervisor_full_name: r.supervisor ? `${r.supervisor.user.first_name} ${r.supervisor.user.last_name}` : null, +// })), +// ); +// } - async findOneProfile(email: string): Promise { - const emp = await this.prisma.employees.findFirst({ - where: { user: { email } }, - select: { - user: { - select: { - first_name: true, - last_name: true, - email: true, - phone_number: true, - residence: true, - }, - }, - supervisor: { - select: { - user: { - select: { - first_name: true, - last_name: true, - }, - }, - }, - }, - job_title: true, - company_code: true, - first_work_day: true, - last_work_day: true, - } - }); - if (!emp) throw new NotFoundException(`Employee with email ${email} not found`); +// async findOneProfile(email: string): Promise { +// const emp = await this.prisma.employees.findFirst({ +// where: { user: { email } }, +// select: { +// user: { +// select: { +// first_name: true, +// last_name: true, +// email: true, +// phone_number: true, +// residence: true, +// }, +// }, +// supervisor: { +// select: { +// user: { +// select: { +// first_name: true, +// last_name: true, +// }, +// }, +// }, +// }, +// job_title: true, +// company_code: true, +// first_work_day: true, +// last_work_day: true, +// } +// }); +// if (!emp) throw new NotFoundException(`Employee with email ${email} not found`); - return { - first_name: emp.user.first_name, - last_name: emp.user.last_name, - email: emp.user.email, - residence: emp.user.residence, - phone_number: emp.user.phone_number, - company_name: emp.company_code, - job_title: emp.job_title, - employee_full_name: `${emp.user.first_name} ${emp.user.last_name}`, - first_work_day: emp.first_work_day.toISOString().slice(0, 10), - last_work_day: emp.last_work_day ? emp.last_work_day.toISOString().slice(0, 10) : null, - supervisor_full_name: emp.supervisor ? `${emp.supervisor.user.first_name}, ${emp.supervisor.user.last_name}` : null, - }; - } +// return { +// first_name: emp.user.first_name, +// last_name: emp.user.last_name, +// email: emp.user.email, +// residence: emp.user.residence, +// phone_number: emp.user.phone_number, +// company_name: emp.company_code, +// job_title: emp.job_title, +// employee_full_name: `${emp.user.first_name} ${emp.user.last_name}`, +// first_work_day: emp.first_work_day.toISOString().slice(0, 10), +// last_work_day: emp.last_work_day ? emp.last_work_day.toISOString().slice(0, 10) : null, +// supervisor_full_name: emp.supervisor ? `${emp.supervisor.user.first_name}, ${emp.supervisor.user.last_name}` : null, +// }; +// } - //_____________________________________________________________________________________________ - // Deprecated or unused methods - //_____________________________________________________________________________________________ +// //_____________________________________________________________________________________________ +// // Deprecated or unused methods +// //_____________________________________________________________________________________________ - // async create(dto: CreateEmployeeDto): Promise { - // const { - // first_name, - // last_name, - // email, - // phone_number, - // residence, - // external_payroll_id, - // company_code, - // job_title, - // first_work_day, - // last_work_day, - // is_supervisor, - // } = dto; +// // async create(dto: CreateEmployeeDto): Promise { +// // const { +// // first_name, +// // last_name, +// // email, +// // phone_number, +// // residence, +// // external_payroll_id, +// // company_code, +// // job_title, +// // first_work_day, +// // last_work_day, +// // is_supervisor, +// // } = dto; - // return this.prisma.$transaction(async (transaction) => { - // const user: Users = await transaction.users.create({ - // data: { - // first_name, - // last_name, - // email, - // phone_number, - // residence, - // }, - // }); - // return transaction.employees.create({ - // data: { - // user_id: user.id, - // external_payroll_id, - // company_code, - // job_title, - // first_work_day, - // last_work_day, - // is_supervisor, - // }, - // }); - // }); - // } +// // return this.prisma.$transaction(async (transaction) => { +// // const user: Users = await transaction.users.create({ +// // data: { +// // first_name, +// // last_name, +// // email, +// // phone_number, +// // residence, +// // }, +// // }); +// // return transaction.employees.create({ +// // data: { +// // user_id: user.id, +// // external_payroll_id, +// // company_code, +// // job_title, +// // first_work_day, +// // last_work_day, +// // is_supervisor, +// // }, +// // }); +// // }); +// // } - // findAll(): Promise { - // return this.prisma.employees.findMany({ - // include: { user: true }, - // }); - // } +// // findAll(): Promise { +// // return this.prisma.employees.findMany({ +// // include: { user: true }, +// // }); +// // } - // async findOne(email: string): Promise { - // const emp = await this.prisma.employees.findFirst({ - // where: { user: { email } }, - // include: { user: true }, - // }); +// // async findOne(email: string): Promise { +// // const emp = await this.prisma.employees.findFirst({ +// // where: { user: { email } }, +// // include: { user: true }, +// // }); - // //add search for archived employees - // if (!emp) { - // throw new NotFoundException(`Employee with email: ${email} not found`); - // } - // return emp; - // } +// // //add search for archived employees +// // if (!emp) { +// // throw new NotFoundException(`Employee with email: ${email} not found`); +// // } +// // return emp; +// // } - // async update( - // email: string, - // dto: UpdateEmployeeDto, - // ): Promise { - // const emp = await this.findOne(email); +// // async update( +// // email: string, +// // dto: UpdateEmployeeDto, +// // ): Promise { +// // const emp = await this.findOne(email); - // const { - // first_name, - // last_name, - // phone_number, - // residence, - // external_payroll_id, - // company_code, - // job_title, - // first_work_day, - // last_work_day, - // is_supervisor, - // email: new_email, - // } = dto; +// // const { +// // first_name, +// // last_name, +// // phone_number, +// // residence, +// // external_payroll_id, +// // company_code, +// // job_title, +// // first_work_day, +// // last_work_day, +// // is_supervisor, +// // email: new_email, +// // } = dto; - // return this.prisma.$transaction(async (transaction) => { - // if( - // first_name !== undefined || - // last_name !== undefined || - // new_email !== undefined || - // phone_number !== undefined || - // residence !== undefined - // ){ - // await transaction.users.update({ - // where: { id: emp.user_id }, - // data: { - // ...(first_name !== undefined && { first_name }), - // ...(last_name !== undefined && { last_name }), - // ...(email !== undefined && { email }), - // ...(phone_number !== undefined && { phone_number }), - // ...(residence !== undefined && { residence }), - // }, - // }); - // } +// // return this.prisma.$transaction(async (transaction) => { +// // if( +// // first_name !== undefined || +// // last_name !== undefined || +// // new_email !== undefined || +// // phone_number !== undefined || +// // residence !== undefined +// // ){ +// // await transaction.users.update({ +// // where: { id: emp.user_id }, +// // data: { +// // ...(first_name !== undefined && { first_name }), +// // ...(last_name !== undefined && { last_name }), +// // ...(email !== undefined && { email }), +// // ...(phone_number !== undefined && { phone_number }), +// // ...(residence !== undefined && { residence }), +// // }, +// // }); +// // } - // const updated = await transaction.employees.update({ - // where: { id: emp.id }, - // data: { - // ...(external_payroll_id !== undefined && { external_payroll_id }), - // ...(company_code !== undefined && { company_code }), - // ...(first_work_day !== undefined && { first_work_day }), - // ...(last_work_day !== undefined && { last_work_day }), - // ...(job_title !== undefined && { job_title }), - // ...(is_supervisor !== undefined && { is_supervisor }), - // }, - // }); - // return updated; - // }); - // } +// // const updated = await transaction.employees.update({ +// // where: { id: emp.id }, +// // data: { +// // ...(external_payroll_id !== undefined && { external_payroll_id }), +// // ...(company_code !== undefined && { company_code }), +// // ...(first_work_day !== undefined && { first_work_day }), +// // ...(last_work_day !== undefined && { last_work_day }), +// // ...(job_title !== undefined && { job_title }), +// // ...(is_supervisor !== undefined && { is_supervisor }), +// // }, +// // }); +// // return updated; +// // }); +// // } - // async remove(email: string): Promise { +// // async remove(email: string): Promise { - // const emp = await this.findOne(email); +// // const emp = await this.findOne(email); - // return this.prisma.$transaction(async (transaction) => { - // await transaction.employees.updateMany({ - // where: { supervisor_id: emp.id }, - // data: { supervisor_id: null }, - // }); - // const deleted_employee = await transaction.employees.delete({ - // where: {id: emp.id }, - // }); - // await transaction.users.delete({ - // where: { id: emp.user_id }, - // }); - // return deleted_employee; - // }); - // } +// // return this.prisma.$transaction(async (transaction) => { +// // await transaction.employees.updateMany({ +// // where: { supervisor_id: emp.id }, +// // data: { supervisor_id: null }, +// // }); +// // const deleted_employee = await transaction.employees.delete({ +// // where: {id: emp.id }, +// // }); +// // await transaction.users.delete({ +// // where: { id: emp.user_id }, +// // }); +// // return deleted_employee; +// // }); +// // } -} \ No newline at end of file +// } \ No newline at end of file diff --git a/src/modules/employees/utils/employee.utils.ts b/src/modules/employees/utils/employee.utils.ts index 3534f3d..04f2540 100644 --- a/src/modules/employees/utils/employee.utils.ts +++ b/src/modules/employees/utils/employee.utils.ts @@ -1,9 +1,9 @@ -export function toDateOrNull(v?: string | null): Date | null { - if (!v) return null; - const day = new Date(v); - return isNaN(day.getTime()) ? null : day; -} -export function toDateOrUndefined(v?: string | null): Date | undefined { - const day = toDateOrNull(v ?? undefined); - return day === null ? undefined : day; -} \ No newline at end of file +// export function toDateOrNull(v?: string | null): Date | null { +// if (!v) return null; +// const day = new Date(v); +// return isNaN(day.getTime()) ? null : day; +// } +// export function toDateOrUndefined(v?: string | null): Date | undefined { +// const day = toDateOrNull(v ?? undefined); +// return day === null ? undefined : day; +// } \ No newline at end of file From 6c44bb5ad2aebf8dbd9e48c024d2a8d3ffd82c81 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 27 Oct 2025 09:50:22 -0400 Subject: [PATCH 06/57] refactor(modules): moved some module to change the structure to domain oriented --- docs/swagger/swagger-spec.json | 24 - src/app.module.ts | 8 +- .../authentication/auth.module.ts | 0 .../controllers/auth.controller.ts | 52 +- .../guards/authentik-auth.guard.ts | 22 +- .../serializers/express-session.serializer.ts | 0 .../services/authentik-auth.service.ts | 28 +- .../strategies/authentik.strategy.ts | 126 ++--- .../controllers/employees.controller.ts | 0 .../employees/dtos/create-employee.dto.ts | 0 .../employees/dtos/list-employee.dto.ts | 0 .../employees/dtos/profil-employee.dto.ts | 0 .../employees/dtos/update-employee.dto.ts | 0 .../employees/employees.module.ts | 5 +- .../services/employees-archival.service.ts | 0 .../employees/services/employees.service.ts | 0 .../employees/utils/employee.utils.ts | 0 .../controllers/oauth-sessions.controller.ts | 0 .../dtos/create-oauth-session.dto.ts | 0 .../dtos/update-oauth-session.dto.ts | 0 .../oauth-sessions/oauth-sessions.module.ts | 0 .../services/oauth-sessions.service.ts | 0 .../controllers/preferences.controller.ts | 0 .../preferences/dtos/preferences.dto.ts | 0 .../preferences/preferences.module.ts | 0 .../services/preferences.service.ts | 0 .../users-management/dtos/user.dto.ts | 0 .../services/abstract-user.service.ts | 0 .../services/users.service.ts | 0 .../users-management/users.module.ts | 0 .../controllers/customers.controller.ts | 68 --- src/modules/customers/customers.module.ts | 10 - .../customers/dtos/create-customer.dto.ts | 79 ---- .../customers/dtos/update-customer.dto.ts | 4 - .../customers/services/customers.service.ts | 93 ---- src/modules/employees/employees.module.ts | 12 - .../controllers/leave-requests.controller.ts | 48 +- .../leave-requests/leave-requests.module.ts | 55 +-- .../holiday-leave-requests.service.ts | 134 +++--- .../services/leave-request.service.ts | 447 +++++++++--------- .../services/sick-leave-requests.service.ts | 175 +++---- .../vacation-leave-requests.service.ts | 168 +++---- .../utils/leave-request.util.ts | 186 ++++---- .../time-and-attendance.module.ts | 27 +- 44 files changed, 739 insertions(+), 1032 deletions(-) rename src/{modules => identity-and-account}/authentication/auth.module.ts (100%) rename src/{modules => identity-and-account}/authentication/controllers/auth.controller.ts (96%) rename src/{modules => identity-and-account}/authentication/guards/authentik-auth.guard.ts (97%) rename src/{modules => identity-and-account}/authentication/serializers/express-session.serializer.ts (100%) rename src/{modules => identity-and-account}/authentication/services/authentik-auth.service.ts (75%) rename src/{modules => identity-and-account}/authentication/strategies/authentik.strategy.ts (97%) rename src/{modules => identity-and-account}/employees/controllers/employees.controller.ts (100%) rename src/{modules => identity-and-account}/employees/dtos/create-employee.dto.ts (100%) rename src/{modules => identity-and-account}/employees/dtos/list-employee.dto.ts (100%) rename src/{modules => identity-and-account}/employees/dtos/profil-employee.dto.ts (100%) rename src/{modules => identity-and-account}/employees/dtos/update-employee.dto.ts (100%) rename src/{modules => identity-and-account}/employees/services/employees-archival.service.ts (100%) rename src/{modules => identity-and-account}/employees/services/employees.service.ts (100%) rename src/{modules => identity-and-account}/employees/utils/employee.utils.ts (100%) rename src/{modules => identity-and-account}/oauth-sessions/controllers/oauth-sessions.controller.ts (100%) rename src/{modules => identity-and-account}/oauth-sessions/dtos/create-oauth-session.dto.ts (100%) rename src/{modules => identity-and-account}/oauth-sessions/dtos/update-oauth-session.dto.ts (100%) rename src/{modules => identity-and-account}/oauth-sessions/oauth-sessions.module.ts (100%) rename src/{modules => identity-and-account}/oauth-sessions/services/oauth-sessions.service.ts (100%) rename src/{modules => identity-and-account}/preferences/controllers/preferences.controller.ts (100%) rename src/{modules => identity-and-account}/preferences/dtos/preferences.dto.ts (100%) rename src/{modules => identity-and-account}/preferences/preferences.module.ts (100%) rename src/{modules => identity-and-account}/preferences/services/preferences.service.ts (100%) rename src/{modules => identity-and-account}/users-management/dtos/user.dto.ts (100%) rename src/{modules => identity-and-account}/users-management/services/abstract-user.service.ts (100%) rename src/{modules => identity-and-account}/users-management/services/users.service.ts (100%) rename src/{modules => identity-and-account}/users-management/users.module.ts (100%) delete mode 100644 src/modules/customers/controllers/customers.controller.ts delete mode 100644 src/modules/customers/customers.module.ts delete mode 100644 src/modules/customers/dtos/create-customer.dto.ts delete mode 100644 src/modules/customers/dtos/update-customer.dto.ts delete mode 100644 src/modules/customers/services/customers.service.ts delete mode 100644 src/modules/employees/employees.module.ts diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 0462102..85ce3df 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -828,30 +828,6 @@ "Expense" ] } - }, - "/archives/expenses": { - "get": { - "operationId": "ExpensesArchiveController_findOneArchived", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], - "responses": { - "200": { - "description": "Archived expense found" - } - }, - "summary": "Fetch expense in archives with its Id", - "tags": [ - "Expense Archives" - ] - } } }, "info": { diff --git a/src/app.module.ts b/src/app.module.ts index 4519929..c8035c4 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -2,17 +2,17 @@ import { BadRequestException, Module, ValidationPipe } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; // import { ArchivalModule } from './modules/archival/archival.module'; -import { AuthenticationModule } from './modules/authentication/auth.module'; +import { AuthenticationModule } from './identity-and-account/authentication/auth.module'; // import { BankCodesModule } from './modules/bank-codes/bank-codes.module'; // import { CsvExportModule } from './modules/exports/csv-exports.module'; import { HealthModule } from './health/health.module'; import { HealthController } from './health/health.controller'; import { NotificationsModule } from './modules/notifications/notifications.module'; -import { OauthSessionsModule } from './modules/oauth-sessions/oauth-sessions.module'; -import { PreferencesModule } from './modules/preferences/preferences.module'; +import { OauthSessionsModule } from './identity-and-account/oauth-sessions/oauth-sessions.module'; +import { PreferencesModule } from './identity-and-account/preferences/preferences.module'; import { PrismaModule } from './prisma/prisma.module'; import { ScheduleModule } from '@nestjs/schedule'; -import { UsersModule } from './modules/users-management/users.module'; +import { UsersModule } from './identity-and-account/users-management/users.module'; import { ConfigModule } from '@nestjs/config'; import { APP_FILTER, APP_PIPE } from '@nestjs/core'; import { HttpExceptionFilter } from './common/filters/http-exception.filter'; diff --git a/src/modules/authentication/auth.module.ts b/src/identity-and-account/authentication/auth.module.ts similarity index 100% rename from src/modules/authentication/auth.module.ts rename to src/identity-and-account/authentication/auth.module.ts diff --git a/src/modules/authentication/controllers/auth.controller.ts b/src/identity-and-account/authentication/controllers/auth.controller.ts similarity index 96% rename from src/modules/authentication/controllers/auth.controller.ts rename to src/identity-and-account/authentication/controllers/auth.controller.ts index 248c4d1..15255f1 100644 --- a/src/modules/authentication/controllers/auth.controller.ts +++ b/src/identity-and-account/authentication/controllers/auth.controller.ts @@ -1,26 +1,26 @@ -import { Controller, Get, Req, Res, UnauthorizedException, UseGuards } from '@nestjs/common'; -import { OIDCLoginGuard } from '../guards/authentik-auth.guard'; -import { Request, Response } from 'express'; - -@Controller('auth') -export class AuthController { - - @UseGuards(OIDCLoginGuard) - @Get('/v1/login') - login() { } - - @Get('/callback') - @UseGuards(OIDCLoginGuard) - loginCallback(@Req() req: Request, @Res() res: Response) { - res.redirect('http://localhost:9000/#/login-success'); - } - - @Get('/me') - getProfile(@Req() req: Request) { - if (!req.user) { - throw new UnauthorizedException('Not logged in'); - } - return req.user; - } - -} +import { Controller, Get, Req, Res, UnauthorizedException, UseGuards } from '@nestjs/common'; +import { OIDCLoginGuard } from '../guards/authentik-auth.guard'; +import { Request, Response } from 'express'; + +@Controller('auth') +export class AuthController { + + @UseGuards(OIDCLoginGuard) + @Get('/v1/login') + login() { } + + @Get('/callback') + @UseGuards(OIDCLoginGuard) + loginCallback(@Req() req: Request, @Res() res: Response) { + res.redirect('http://localhost:9000/#/login-success'); + } + + @Get('/me') + getProfile(@Req() req: Request) { + if (!req.user) { + throw new UnauthorizedException('Not logged in'); + } + return req.user; + } + +} diff --git a/src/modules/authentication/guards/authentik-auth.guard.ts b/src/identity-and-account/authentication/guards/authentik-auth.guard.ts similarity index 97% rename from src/modules/authentication/guards/authentik-auth.guard.ts rename to src/identity-and-account/authentication/guards/authentik-auth.guard.ts index df9208c..ff4f44d 100644 --- a/src/modules/authentication/guards/authentik-auth.guard.ts +++ b/src/identity-and-account/authentication/guards/authentik-auth.guard.ts @@ -1,12 +1,12 @@ -import { ExecutionContext, Injectable } from '@nestjs/common'; -import { AuthGuard } from '@nestjs/passport'; - -@Injectable() -export class OIDCLoginGuard extends AuthGuard('openidconnect') { - async canActivate(context: ExecutionContext) { - const result = (await super.canActivate(context)) as boolean; - const request = context.switchToHttp().getRequest(); - await super.logIn(request); - return result; - } +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +@Injectable() +export class OIDCLoginGuard extends AuthGuard('openidconnect') { + async canActivate(context: ExecutionContext) { + const result = (await super.canActivate(context)) as boolean; + const request = context.switchToHttp().getRequest(); + await super.logIn(request); + return result; + } } \ No newline at end of file diff --git a/src/modules/authentication/serializers/express-session.serializer.ts b/src/identity-and-account/authentication/serializers/express-session.serializer.ts similarity index 100% rename from src/modules/authentication/serializers/express-session.serializer.ts rename to src/identity-and-account/authentication/serializers/express-session.serializer.ts diff --git a/src/modules/authentication/services/authentik-auth.service.ts b/src/identity-and-account/authentication/services/authentik-auth.service.ts similarity index 75% rename from src/modules/authentication/services/authentik-auth.service.ts rename to src/identity-and-account/authentication/services/authentik-auth.service.ts index 9d6514c..974a1eb 100644 --- a/src/modules/authentication/services/authentik-auth.service.ts +++ b/src/identity-and-account/authentication/services/authentik-auth.service.ts @@ -1,14 +1,14 @@ - -import { Injectable } from '@nestjs/common'; -import { UsersService } from 'src/modules/users-management/services/users.service'; - -@Injectable() -export class AuthentikAuthService { - constructor(private usersService: UsersService) {} - - async validateUser(user_email: string): Promise { - const user = await this.usersService.findOneByEmail(user_email); - - return user; - } -} + +import { Injectable } from '@nestjs/common'; +import { UsersService } from 'src/identity-and-account/users-management/services/users.service'; + +@Injectable() +export class AuthentikAuthService { + constructor(private usersService: UsersService) {} + + async validateUser(user_email: string): Promise { + const user = await this.usersService.findOneByEmail(user_email); + + return user; + } +} diff --git a/src/modules/authentication/strategies/authentik.strategy.ts b/src/identity-and-account/authentication/strategies/authentik.strategy.ts similarity index 97% rename from src/modules/authentication/strategies/authentik.strategy.ts rename to src/identity-and-account/authentication/strategies/authentik.strategy.ts index 98602c5..6609356 100644 --- a/src/modules/authentication/strategies/authentik.strategy.ts +++ b/src/identity-and-account/authentication/strategies/authentik.strategy.ts @@ -1,63 +1,63 @@ - -import { Strategy as OIDCStrategy, Profile, VerifyCallback } from 'passport-openidconnect'; -import { PassportStrategy } from '@nestjs/passport'; -import { Injectable } from '@nestjs/common'; -import { AuthentikAuthService } from '../services/authentik-auth.service'; -import { ValidationError } from 'class-validator'; - -export interface AuthentikPayload { - iss: string; // Issuer - sub: string; // Subject (user ID) - aud: string; // Audience (client ID) - exp: number; // Expiration time (Unix) - iat: number; // Issued at time (Unix) - auth_time: number; // Time of authentication (Unix) - acr?: string; // Auth Context Class Reference - amr?: string[]; // Auth Method References (e.g., ['pwd']) - email: string; - email_verified: boolean; - name?: string; - given_name?: string; - preferred_username?: string; - nickname?: string; - groups?: string[]; -} - -@Injectable() -export class AuthentikStrategy extends PassportStrategy(OIDCStrategy, 'openidconnect', 8) { - constructor(private authentikAuthService: AuthentikAuthService) { - super({ - issuer: process.env.AUTHENTIK_ISSUER || "ISSUER_MISSING", - clientID: process.env.AUTHENTIK_CLIENT_ID || 'CLIENT_ID_MISSING', - clientSecret: process.env.AUTHENTIK_CLIENT_SECRET || 'CLIENT_SECRET_MISSING', - callbackURL: process.env.AUTHENTIK_CALLBACK_URL || 'CALLBACK_URL_MISSING', - authorizationURL: process.env.AUTHENTIK_AUTH_URL || 'AUTH_URL_MISSING', - tokenURL: process.env.AUTHENTIK_TOKEN_URL || 'TOKEN_URL_MISSING', - userInfoURL: process.env.AUTHENTIK_USERINFO_URL || 'USERINFO_URL_MISSING', - scope: ['openid', 'email', 'profile', 'offline_access'], - },); - } - - async validate( - _issuer: string, - profile: Profile, - _context: any, - _idToken: string, - _accessToken: string, - _refreshToken: string, - _params: any, - cb: VerifyCallback, - ): Promise { - try { - const email = profile.emails?.[0]?.value; - if (!email) return cb(new Error('Missing email in OIDC profile'), false); - - const user = await this.authentikAuthService.validateUser(email); - if (!user) return cb(new Error('User not found'), false); - - return cb(null, user); - } catch (err) { - return cb(err, false); - } - } -} + +import { Strategy as OIDCStrategy, Profile, VerifyCallback } from 'passport-openidconnect'; +import { PassportStrategy } from '@nestjs/passport'; +import { Injectable } from '@nestjs/common'; +import { AuthentikAuthService } from '../services/authentik-auth.service'; +import { ValidationError } from 'class-validator'; + +export interface AuthentikPayload { + iss: string; // Issuer + sub: string; // Subject (user ID) + aud: string; // Audience (client ID) + exp: number; // Expiration time (Unix) + iat: number; // Issued at time (Unix) + auth_time: number; // Time of authentication (Unix) + acr?: string; // Auth Context Class Reference + amr?: string[]; // Auth Method References (e.g., ['pwd']) + email: string; + email_verified: boolean; + name?: string; + given_name?: string; + preferred_username?: string; + nickname?: string; + groups?: string[]; +} + +@Injectable() +export class AuthentikStrategy extends PassportStrategy(OIDCStrategy, 'openidconnect', 8) { + constructor(private authentikAuthService: AuthentikAuthService) { + super({ + issuer: process.env.AUTHENTIK_ISSUER || "ISSUER_MISSING", + clientID: process.env.AUTHENTIK_CLIENT_ID || 'CLIENT_ID_MISSING', + clientSecret: process.env.AUTHENTIK_CLIENT_SECRET || 'CLIENT_SECRET_MISSING', + callbackURL: process.env.AUTHENTIK_CALLBACK_URL || 'CALLBACK_URL_MISSING', + authorizationURL: process.env.AUTHENTIK_AUTH_URL || 'AUTH_URL_MISSING', + tokenURL: process.env.AUTHENTIK_TOKEN_URL || 'TOKEN_URL_MISSING', + userInfoURL: process.env.AUTHENTIK_USERINFO_URL || 'USERINFO_URL_MISSING', + scope: ['openid', 'email', 'profile', 'offline_access'], + },); + } + + async validate( + _issuer: string, + profile: Profile, + _context: any, + _idToken: string, + _accessToken: string, + _refreshToken: string, + _params: any, + cb: VerifyCallback, + ): Promise { + try { + const email = profile.emails?.[0]?.value; + if (!email) return cb(new Error('Missing email in OIDC profile'), false); + + const user = await this.authentikAuthService.validateUser(email); + if (!user) return cb(new Error('User not found'), false); + + return cb(null, user); + } catch (err) { + return cb(err, false); + } + } +} diff --git a/src/modules/employees/controllers/employees.controller.ts b/src/identity-and-account/employees/controllers/employees.controller.ts similarity index 100% rename from src/modules/employees/controllers/employees.controller.ts rename to src/identity-and-account/employees/controllers/employees.controller.ts diff --git a/src/modules/employees/dtos/create-employee.dto.ts b/src/identity-and-account/employees/dtos/create-employee.dto.ts similarity index 100% rename from src/modules/employees/dtos/create-employee.dto.ts rename to src/identity-and-account/employees/dtos/create-employee.dto.ts diff --git a/src/modules/employees/dtos/list-employee.dto.ts b/src/identity-and-account/employees/dtos/list-employee.dto.ts similarity index 100% rename from src/modules/employees/dtos/list-employee.dto.ts rename to src/identity-and-account/employees/dtos/list-employee.dto.ts diff --git a/src/modules/employees/dtos/profil-employee.dto.ts b/src/identity-and-account/employees/dtos/profil-employee.dto.ts similarity index 100% rename from src/modules/employees/dtos/profil-employee.dto.ts rename to src/identity-and-account/employees/dtos/profil-employee.dto.ts diff --git a/src/modules/employees/dtos/update-employee.dto.ts b/src/identity-and-account/employees/dtos/update-employee.dto.ts similarity index 100% rename from src/modules/employees/dtos/update-employee.dto.ts rename to src/identity-and-account/employees/dtos/update-employee.dto.ts diff --git a/src/identity-and-account/employees/employees.module.ts b/src/identity-and-account/employees/employees.module.ts index 7ce6f04..676f4b0 100644 --- a/src/identity-and-account/employees/employees.module.ts +++ b/src/identity-and-account/employees/employees.module.ts @@ -1,13 +1,12 @@ // import { Module } from '@nestjs/common'; // import { EmployeesController } from './controllers/employees.controller'; // import { EmployeesService } from './services/employees.service'; -// import { EmployeesArchivalService } from './services/employees-archival.service'; // import { SharedModule } from '../../time-and-attendance/modules/shared/shared.module'; // @Module({ // imports: [SharedModule], // controllers: [EmployeesController], -// providers: [EmployeesService, EmployeesArchivalService], -// exports: [EmployeesService, EmployeesArchivalService], +// providers: [EmployeesService], +// exports: [EmployeesService ], // }) // export class EmployeesModule {} diff --git a/src/modules/employees/services/employees-archival.service.ts b/src/identity-and-account/employees/services/employees-archival.service.ts similarity index 100% rename from src/modules/employees/services/employees-archival.service.ts rename to src/identity-and-account/employees/services/employees-archival.service.ts diff --git a/src/modules/employees/services/employees.service.ts b/src/identity-and-account/employees/services/employees.service.ts similarity index 100% rename from src/modules/employees/services/employees.service.ts rename to src/identity-and-account/employees/services/employees.service.ts diff --git a/src/modules/employees/utils/employee.utils.ts b/src/identity-and-account/employees/utils/employee.utils.ts similarity index 100% rename from src/modules/employees/utils/employee.utils.ts rename to src/identity-and-account/employees/utils/employee.utils.ts diff --git a/src/modules/oauth-sessions/controllers/oauth-sessions.controller.ts b/src/identity-and-account/oauth-sessions/controllers/oauth-sessions.controller.ts similarity index 100% rename from src/modules/oauth-sessions/controllers/oauth-sessions.controller.ts rename to src/identity-and-account/oauth-sessions/controllers/oauth-sessions.controller.ts diff --git a/src/modules/oauth-sessions/dtos/create-oauth-session.dto.ts b/src/identity-and-account/oauth-sessions/dtos/create-oauth-session.dto.ts similarity index 100% rename from src/modules/oauth-sessions/dtos/create-oauth-session.dto.ts rename to src/identity-and-account/oauth-sessions/dtos/create-oauth-session.dto.ts diff --git a/src/modules/oauth-sessions/dtos/update-oauth-session.dto.ts b/src/identity-and-account/oauth-sessions/dtos/update-oauth-session.dto.ts similarity index 100% rename from src/modules/oauth-sessions/dtos/update-oauth-session.dto.ts rename to src/identity-and-account/oauth-sessions/dtos/update-oauth-session.dto.ts diff --git a/src/modules/oauth-sessions/oauth-sessions.module.ts b/src/identity-and-account/oauth-sessions/oauth-sessions.module.ts similarity index 100% rename from src/modules/oauth-sessions/oauth-sessions.module.ts rename to src/identity-and-account/oauth-sessions/oauth-sessions.module.ts diff --git a/src/modules/oauth-sessions/services/oauth-sessions.service.ts b/src/identity-and-account/oauth-sessions/services/oauth-sessions.service.ts similarity index 100% rename from src/modules/oauth-sessions/services/oauth-sessions.service.ts rename to src/identity-and-account/oauth-sessions/services/oauth-sessions.service.ts diff --git a/src/modules/preferences/controllers/preferences.controller.ts b/src/identity-and-account/preferences/controllers/preferences.controller.ts similarity index 100% rename from src/modules/preferences/controllers/preferences.controller.ts rename to src/identity-and-account/preferences/controllers/preferences.controller.ts diff --git a/src/modules/preferences/dtos/preferences.dto.ts b/src/identity-and-account/preferences/dtos/preferences.dto.ts similarity index 100% rename from src/modules/preferences/dtos/preferences.dto.ts rename to src/identity-and-account/preferences/dtos/preferences.dto.ts diff --git a/src/modules/preferences/preferences.module.ts b/src/identity-and-account/preferences/preferences.module.ts similarity index 100% rename from src/modules/preferences/preferences.module.ts rename to src/identity-and-account/preferences/preferences.module.ts diff --git a/src/modules/preferences/services/preferences.service.ts b/src/identity-and-account/preferences/services/preferences.service.ts similarity index 100% rename from src/modules/preferences/services/preferences.service.ts rename to src/identity-and-account/preferences/services/preferences.service.ts diff --git a/src/modules/users-management/dtos/user.dto.ts b/src/identity-and-account/users-management/dtos/user.dto.ts similarity index 100% rename from src/modules/users-management/dtos/user.dto.ts rename to src/identity-and-account/users-management/dtos/user.dto.ts diff --git a/src/modules/users-management/services/abstract-user.service.ts b/src/identity-and-account/users-management/services/abstract-user.service.ts similarity index 100% rename from src/modules/users-management/services/abstract-user.service.ts rename to src/identity-and-account/users-management/services/abstract-user.service.ts diff --git a/src/modules/users-management/services/users.service.ts b/src/identity-and-account/users-management/services/users.service.ts similarity index 100% rename from src/modules/users-management/services/users.service.ts rename to src/identity-and-account/users-management/services/users.service.ts diff --git a/src/modules/users-management/users.module.ts b/src/identity-and-account/users-management/users.module.ts similarity index 100% rename from src/modules/users-management/users.module.ts rename to src/identity-and-account/users-management/users.module.ts diff --git a/src/modules/customers/controllers/customers.controller.ts b/src/modules/customers/controllers/customers.controller.ts deleted file mode 100644 index c066342..0000000 --- a/src/modules/customers/controllers/customers.controller.ts +++ /dev/null @@ -1,68 +0,0 @@ -// import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from '@nestjs/common'; -// import { CustomersService } from '../services/customers.service'; -// import { Customers } from '@prisma/client'; -// import { CreateCustomerDto } from '../dtos/create-customer.dto'; -// import { UpdateCustomerDto } from '../dtos/update-customer.dto'; -// import { RolesAllowed } from "src/common/decorators/roles.decorators"; -// import { Roles as RoleEnum } from '.prisma/client'; -// import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; - -// @ApiTags('Customers') -// @ApiBearerAuth('access-token') -// // @UseGuards() -// @Controller('customers') -// export class CustomersController { -// constructor(private readonly customersService: CustomersService) {} - -// //_____________________________________________________________________________________________ -// // Deprecated or unused methods -// //_____________________________________________________________________________________________ - -// // @Post() -// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.SUPERVISOR) -// // @ApiOperation({ summary: 'Create customer' }) -// // @ApiResponse({ status: 201, description: 'Customer created', type: CreateCustomerDto }) -// // @ApiResponse({ status: 400, description: 'Invalid task or invalid data' }) -// // create(@Body() dto: CreateCustomerDto): Promise { -// // return this.customersService.create(dto); -// // } - -// // @Get() -// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) -// // @ApiOperation({ summary: 'Find all customers' }) -// // @ApiResponse({ status: 201, description: 'List of customers found', type: CreateCustomerDto, isArray: true }) -// // @ApiResponse({ status: 400, description: 'List of customers not found' }) -// // findAll(): Promise { -// // return this.customersService.findAll(); -// // } - -// // @Get(':id') -// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) -// // @ApiOperation({ summary: 'Find customer' }) -// // @ApiResponse({ status: 201, description: 'Customer found', type: CreateCustomerDto }) -// // @ApiResponse({ status: 400, description: 'Customer not found' }) -// // findOne(@Param('id', ParseIntPipe) id: number): Promise { -// // return this.customersService.findOne(id); -// // } - -// // @Patch(':id') -// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE,RoleEnum.SUPERVISOR) -// // @ApiOperation({ summary: 'Update customer' }) -// // @ApiResponse({ status: 201, description: 'Customer updated', type: CreateCustomerDto }) -// // @ApiResponse({ status: 400, description: 'Customer not found' }) -// // update( -// // @Param('id', ParseIntPipe) id: number, -// // @Body() dto: UpdateCustomerDto, -// // ): Promise { -// // return this.customersService.update(id, dto); -// // } - -// // @Delete(':id') -// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.SUPERVISOR) -// // @ApiOperation({ summary: 'Delete customer' }) -// // @ApiResponse({ status: 201, description: 'Customer deleted', type: CreateCustomerDto }) -// // @ApiResponse({ status: 400, description: 'Customer not found' }) -// // remove(@Param('id', ParseIntPipe) id: number): Promise{ -// // return this.customersService.remove(id); -// // } -// } diff --git a/src/modules/customers/customers.module.ts b/src/modules/customers/customers.module.ts deleted file mode 100644 index cccb363..0000000 --- a/src/modules/customers/customers.module.ts +++ /dev/null @@ -1,10 +0,0 @@ - -// import { Module } from '@nestjs/common'; -// import { CustomersController } from './controllers/customers.controller'; -// import { CustomersService } from './services/customers.service'; - -// @Module({ -// controllers:[CustomersController], -// providers:[CustomersService], -// }) -// export class CustomersModule {} \ No newline at end of file diff --git a/src/modules/customers/dtos/create-customer.dto.ts b/src/modules/customers/dtos/create-customer.dto.ts deleted file mode 100644 index 8a136ac..0000000 --- a/src/modules/customers/dtos/create-customer.dto.ts +++ /dev/null @@ -1,79 +0,0 @@ -// import { ApiProperty } from "@nestjs/swagger"; -// import { Type } from "class-transformer"; -// import { -// Allow, -// IsEmail, -// IsInt, -// IsNotEmpty, -// IsOptional, -// IsPositive, -// IsString, -// IsUUID, -// } from "class-validator"; - -// export class CreateCustomerDto { -// @ApiProperty({ -// example: 1, -// description: 'Unique ID of a customer(primary-key, auto-incremented)', -// }) -// @Allow() -// id?: number; - -// @ApiProperty({ -// example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d', -// description: 'UUID of the user linked to that customer', -// }) -// @IsUUID() -// @IsOptional() -// user_id?: string; - -// @ApiProperty({ -// example: 'Gandalf', -// description: 'Customer`s first name', -// }) -// @IsString() -// @IsNotEmpty() -// first_name: string; - -// @ApiProperty({ -// example: 'TheGray', -// description: 'Customer`s last name', -// }) -// @IsString() -// @IsNotEmpty() -// last_name: string; - -// @ApiProperty({ -// example: 'you_shall_not_pass@middleEarth.com', -// description: 'Customer`s email', -// }) -// @IsEmail() -// @IsOptional() -// email: string; - -// @ApiProperty({ -// example: '8436637464', -// description: 'Customer`s phone number', -// }) -// @IsString() -// phone_number: string; - -// @ApiProperty({ -// example: '1 Ringbearer`s way, Mount Doom city, ME, T1R 1N6 ', -// description: 'Customer`s residence', -// required: false, -// }) -// @IsString() -// @IsOptional() -// residence?: string; - -// @ApiProperty({ -// example: '4263253', -// description: 'Customer`s invoice number', -// required: false, -// }) -// @Type(() => Number) -// @IsInt() -// @IsNotEmpty() -// invoice_id: number; -// } \ No newline at end of file diff --git a/src/modules/customers/dtos/update-customer.dto.ts b/src/modules/customers/dtos/update-customer.dto.ts deleted file mode 100644 index 2f52413..0000000 --- a/src/modules/customers/dtos/update-customer.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -// import { PartialType } from "@nestjs/swagger"; -// import { CreateCustomerDto } from "./create-customer.dto"; - -// export class UpdateCustomerDto extends PartialType(CreateCustomerDto) {} \ No newline at end of file diff --git a/src/modules/customers/services/customers.service.ts b/src/modules/customers/services/customers.service.ts deleted file mode 100644 index 2662250..0000000 --- a/src/modules/customers/services/customers.service.ts +++ /dev/null @@ -1,93 +0,0 @@ -// import { Injectable } from '@nestjs/common'; - -// @Injectable() -// export class CustomersService { - -// //_____________________________________________________________________________________________ -// // Deprecated or unused methods -// //_____________________________________________________________________________________________ - -// // constructor(private readonly prisma: PrismaService) {} - -// // async create(dto: CreateCustomerDto): Promise { -// // const { -// // first_name, -// // last_name, -// // email, -// // phone_number, -// // residence, -// // invoice_id, -// // } = dto; - -// // return this.prisma.$transaction(async (transaction) => { -// // const user: Users = await transaction.users.create({ -// // data: { -// // first_name, -// // last_name, -// // email, -// // phone_number, -// // residence, -// // }, -// // }); -// // return transaction.customers.create({ -// // data: { -// // user_id: user.id, -// // invoice_id, -// // }, -// // }); -// // }); -// // } - -// // findAll(): Promise { -// // return this.prisma.customers.findMany({ -// // include: { user: true }, -// // }) -// // } - -// // async findOne(id:number): Promise { -// // const customer = await this.prisma.customers.findUnique({ -// // where: { id }, -// // include: { user: true }, -// // }); -// // if(!customer) throw new NotFoundException(`Customer #${id} not found`); -// // return customer; -// // } - -// // async update(id: number,dto: UpdateCustomerDto): Promise { -// // const customer = await this.findOne(id); - -// // const { -// // first_name, -// // last_name, -// // email, -// // phone_number, -// // residence, -// // invoice_id, -// // } = dto; - -// // return this.prisma.$transaction(async (transaction) => { -// // await transaction.users.update({ -// // where: { id: customer.user_id }, -// // data: { -// // ...(first_name !== undefined && { first_name }), -// // ...(last_name !== undefined && { last_name }), -// // ...(email !== undefined && { email }), -// // ...(phone_number !== undefined && { phone_number }), -// // ...(residence !== undefined && { residence }), -// // }, -// // }); - -// // return transaction.customers.update({ -// // where: { id }, -// // data: { -// // ...(invoice_id !== undefined && { invoice_id }), -// // }, -// // }); -// // }); -// // } - -// // async remove(id: number): Promise { -// // await this.findOne(id); -// // return this.prisma.customers.delete({ where: { id }}); -// // } -// } diff --git a/src/modules/employees/employees.module.ts b/src/modules/employees/employees.module.ts deleted file mode 100644 index 676f4b0..0000000 --- a/src/modules/employees/employees.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -// import { Module } from '@nestjs/common'; -// import { EmployeesController } from './controllers/employees.controller'; -// import { EmployeesService } from './services/employees.service'; -// import { SharedModule } from '../../time-and-attendance/modules/shared/shared.module'; - -// @Module({ -// imports: [SharedModule], -// controllers: [EmployeesController], -// providers: [EmployeesService], -// exports: [EmployeesService ], -// }) -// export class EmployeesModule {} diff --git a/src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller.ts b/src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller.ts index 7ecce7e..fc934ff 100644 --- a/src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller.ts +++ b/src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller.ts @@ -1,30 +1,30 @@ -// import { Body, Controller, Post } from "@nestjs/common"; -// import { ApiBearerAuth, ApiTags } from "@nestjs/swagger"; -// import { LeaveRequestsService } from "../services/leave-request.service"; -// import { UpsertLeaveRequestDto } from "../dtos/upsert-leave-request.dto"; -// import { LeaveTypes } from "@prisma/client"; +import { Body, Controller, Post } from "@nestjs/common"; +import { ApiBearerAuth, ApiTags } from "@nestjs/swagger"; +import { LeaveRequestsService } from "../services/leave-request.service"; +import { UpsertLeaveRequestDto } from "../dtos/upsert-leave-request.dto"; +import { LeaveTypes } from "@prisma/client"; -// @ApiTags('Leave Requests') -// @ApiBearerAuth('access-token') -// // @UseGuards() -// @Controller('leave-requests') -// export class LeaveRequestController { -// constructor(private readonly leave_service: LeaveRequestsService){} +@ApiTags('Leave Requests') +@ApiBearerAuth('access-token') +// @UseGuards() +@Controller('leave-requests') +export class LeaveRequestController { + constructor(private readonly leave_service: LeaveRequestsService){} -// @Post('upsert') -// async upsertLeaveRequest(@Body() dto: UpsertLeaveRequestDto) { -// const { action, leave_requests } = await this.leave_service.handle(dto); -// return { action, leave_requests }; -// }q + @Post('upsert') + async upsertLeaveRequest(@Body() dto: UpsertLeaveRequestDto) { + const { action, leave_requests } = await this.leave_service.handle(dto); + return { action, leave_requests }; + }q -// //TODO: -// /* -// @Get('archive') -// findAllArchived(){...} + //TODO: + /* + @Get('archive') + findAllArchived(){...} -// @Get('archive/:id') -// findOneArchived(id){...} -// */ + @Get('archive/:id') + findOneArchived(id){...} + */ -// } +} diff --git a/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts b/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts index 03ad546..954f07a 100644 --- a/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts +++ b/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts @@ -1,29 +1,30 @@ -// import { PrismaService } from "src/prisma/prisma.service"; -// import { LeaveRequestController } from "./controllers/leave-requests.controller"; -// import { HolidayLeaveRequestsService } from "./services/holiday-leave-requests.service"; -// import { Module } from "@nestjs/common"; -// import { BusinessLogicsModule } from "src/modules/business-logics/business-logics.module"; -// import { VacationLeaveRequestsService } from "./services/vacation-leave-requests.service"; -// import { SickLeaveRequestsService } from "./services/sick-leave-requests.service"; -// import { LeaveRequestsService } from "./services/leave-request.service"; -// import { ShiftsModule } from "../shifts/shifts.module"; -// import { LeaveRequestsUtils } from "./utils/leave-request.util"; -// import { SharedModule } from "../shared/shared.module"; +import { Module } from "@nestjs/common"; +import { PrismaService } from "src/prisma/prisma.service"; +import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; +import { HolidayService } from "src/time-and-attendance/domains/services/holiday.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 { LeaveRequestController } from "src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller"; +import { LeaveRequestsService } from "src/time-and-attendance/modules/leave-requests/services/leave-request.service"; +import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; +import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; +import { ShiftsModule } from "src/time-and-attendance/modules/time-tracker/shifts/shifts.module"; -// @Module({ -// imports: [BusinessLogicsModule, ShiftsModule, SharedModule], -// controllers: [LeaveRequestController], -// providers: [ -// VacationLeaveRequestsService, -// SickLeaveRequestsService, -// HolidayLeaveRequestsService, -// LeaveRequestsService, -// PrismaService, -// LeaveRequestsUtils, -// ], -// exports: [ -// LeaveRequestsService, -// ], -// }) -// export class LeaveRequestsModule {} \ No newline at end of file +@Module({ + imports: [BusinessLogicsModule, ShiftsModule, SharedModule], + controllers: [LeaveRequestController], + providers: [ + VacationService, + SickLeaveService, + HolidayService, + LeaveRequestsService, + PrismaService, + LeaveRequestsUtils, + ], + exports: [ + LeaveRequestsService, + ], +}) + +export class LeaveRequestsModule {} \ No newline at end of file diff --git a/src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts b/src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts index ecfa8cc..43f4716 100644 --- a/src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts +++ b/src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts @@ -1,78 +1,78 @@ -// import { UpsertLeaveRequestDto, UpsertResult } from '../dtos/upsert-leave-request.dto'; -// import { LeaveRequestViewDto } from '../dtos/leave-request-view.dto'; -// import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; -// import { LeaveApprovalStatus, LeaveTypes } from '@prisma/client'; -// import { HolidayService } from 'src/modules/business-logics/services/holiday.service'; -// import { PrismaService } from 'src/prisma/prisma.service'; -// import { mapRowToView } from '../mappers/leave-requests.mapper'; -// import { leaveRequestsSelect } from '../utils/leave-requests.select'; -// import { LeaveRequestsUtils} from '../utils/leave-request.util'; -// import { normalizeDates, toDateOnly } from 'src/modules/shared/helpers/date-time.helpers'; -// import { BankCodesResolver } from 'src/modules/shared/utils/resolve-bank-type-id.utils'; -// import { EmailToIdResolver } from 'src/modules/shared/utils/resolve-email-id.utils'; +import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; +import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; +import { PrismaService } from "src/prisma/prisma.service"; +import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; +import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto"; +import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto"; +import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper"; +import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; +import { leaveRequestsSelect } from "src/time-and-attendance/modules/leave-requests/utils/leave-requests.select"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; +import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; -// @Injectable() -// export class HolidayLeaveRequestsService { -// constructor( -// private readonly prisma: PrismaService, -// private readonly holidayService: HolidayService, -// private readonly leaveUtils: LeaveRequestsUtils, -// private readonly emailResolver: EmailToIdResolver, -// private readonly typeResolver: BankCodesResolver, -// ) {} +@Injectable() +export class HolidayLeaveRequestsService { + constructor( + private readonly prisma: PrismaService, + private readonly holidayService: HolidayService, + private readonly leaveUtils: LeaveRequestsUtils, + private readonly emailResolver: EmailToIdResolver, + private readonly typeResolver: BankCodesResolver, + ) {} -// async create(dto: UpsertLeaveRequestDto): Promise { -// const email = dto.email.trim(); -// const employee_id = await this.emailResolver.findIdByEmail(email); -// const bank_code = await this.typeResolver.findByType(LeaveTypes.HOLIDAY); -// if(!bank_code) throw new NotFoundException(`bank_code not found`); -// const dates = normalizeDates(dto.dates); -// if (!dates.length) throw new BadRequestException('Dates array must not be empty'); + async create(dto: UpsertLeaveRequestDto): Promise { + const email = dto.email.trim(); + const employee_id = await this.emailResolver.findIdByEmail(email); + const bank_code = await this.typeResolver.findByType(LeaveTypes.HOLIDAY); + if(!bank_code) throw new NotFoundException(`bank_code not found`); + const dates = normalizeDates(dto.dates); + if (!dates.length) throw new BadRequestException('Dates array must not be empty'); -// const created: LeaveRequestViewDto[] = []; + const created: LeaveRequestViewDto[] = []; -// for (const iso_date of dates) { -// const date = toDateOnly(iso_date); + for (const iso_date of dates) { + const date = toDateOnly(iso_date); -// const existing = await this.prisma.leaveRequests.findUnique({ -// where: { -// leave_per_employee_date: { -// employee_id: employee_id, -// leave_type: LeaveTypes.HOLIDAY, -// date, -// }, -// }, -// select: { id: true }, -// }); -// if (existing) { -// throw new BadRequestException(`Holiday request already exists for ${iso_date}`); -// } + const existing = await this.prisma.leaveRequests.findUnique({ + where: { + leave_per_employee_date: { + employee_id: employee_id, + leave_type: LeaveTypes.HOLIDAY, + date, + }, + }, + select: { id: true }, + }); + if (existing) { + throw new BadRequestException(`Holiday request already exists for ${iso_date}`); + } -// const payable = await this.holidayService.calculateHolidayPay(email, date, bank_code.modifier); -// const row = await this.prisma.leaveRequests.create({ -// data: { -// employee_id: employee_id, -// bank_code_id: bank_code.id, -// leave_type: LeaveTypes.HOLIDAY, -// date, -// comment: dto.comment ?? '', -// requested_hours: dto.requested_hours ?? 8, -// payable_hours: payable, -// approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING, -// }, -// select: leaveRequestsSelect, -// }); + const payable = await this.holidayService.calculateHolidayPay(email, date, bank_code.modifier); + const row = await this.prisma.leaveRequests.create({ + data: { + employee_id: employee_id, + bank_code_id: bank_code.id, + leave_type: LeaveTypes.HOLIDAY, + date, + comment: dto.comment ?? '', + requested_hours: dto.requested_hours ?? 8, + payable_hours: payable, + approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING, + }, + select: leaveRequestsSelect, + }); -// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0); -// if (row.approval_status === LeaveApprovalStatus.APPROVED) { -// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours,LeaveTypes.HOLIDAY, row.comment); -// } + const hours = Number(row.payable_hours ?? row.requested_hours ?? 0); + if (row.approval_status === LeaveApprovalStatus.APPROVED) { + await this.leaveUtils.syncShift(email, employee_id, iso_date, hours,LeaveTypes.HOLIDAY, row.comment); + } -// created.push({ ...mapRowToView(row), action: 'create' }); -// } + created.push({ ...mapRowToView(row), action: 'create' }); + } -// return { action: 'create', leave_requests: created }; -// } -// } + return { action: 'create', leave_requests: created }; + } +} diff --git a/src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts b/src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts index 7b0c82e..8b44ac0 100644 --- a/src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts +++ b/src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts @@ -1,248 +1,243 @@ -// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; -// import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; -// import { roundToQuarterHour } from "src/common/utils/date-utils"; -// import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto"; -// import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; -// import { mapRowToView } from "../mappers/leave-requests.mapper"; -// import { leaveRequestsSelect } from "../utils/leave-requests.select"; -// import { HolidayLeaveRequestsService } from "./holiday-leave-requests.service"; -// import { SickLeaveRequestsService } from "./sick-leave-requests.service"; -// import { VacationLeaveRequestsService } from "./vacation-leave-requests.service"; -// import { HolidayService } from "src/modules/business-logics/services/holiday.service"; -// import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service"; -// import { VacationService } from "src/modules/business-logics/services/vacation.service"; -// import { PrismaService } from "src/prisma/prisma.service"; -// import { LeaveRequestsUtils } from "../utils/leave-request.util"; -// import { normalizeDates, toDateOnly, toISODateKey } from "src/modules/shared/helpers/date-time.helpers"; -// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils"; -// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils"; +import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; +import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; +import { roundToQuarterHour } from "src/common/utils/date-utils"; +import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto"; +import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; +import { mapRowToView } from "../mappers/leave-requests.mapper"; +import { leaveRequestsSelect } from "../utils/leave-requests.select"; +import { PrismaService } from "src/prisma/prisma.service"; +import { HolidayService } from "src/time-and-attendance/domains/services/holiday.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 { normalizeDates, toDateOnly, toISODateKey } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; +import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; +@Injectable() +export class LeaveRequestsService { + constructor( + private readonly prisma: PrismaService, + private readonly holidayService: HolidayService, + private readonly sickLogic: SickLeaveService, + private readonly sickLeaveService: SickLeaveService, + private readonly vacationService: VacationService, + private readonly vacationLogic: VacationService, + private readonly leaveUtils: LeaveRequestsUtils, + private readonly emailResolver: EmailToIdResolver, + private readonly typeResolver: BankCodesResolver, + ) {} -// @Injectable() -// export class LeaveRequestsService { -// constructor( -// private readonly prisma: PrismaService, -// private readonly holidayLeaveService: HolidayLeaveRequestsService, -// private readonly holidayService: HolidayService, -// private readonly sickLogic: SickLeaveService, -// private readonly sickLeaveService: SickLeaveRequestsService, -// private readonly vacationLeaveService: VacationLeaveRequestsService, -// private readonly vacationLogic: VacationService, -// private readonly leaveUtils: LeaveRequestsUtils, -// private readonly emailResolver: EmailToIdResolver, -// private readonly typeResolver: BankCodesResolver, -// ) {} + // handle distribution to the right service according to the selected type and action + async handle(dto: UpsertLeaveRequestDto): Promise { + switch (dto.type) { + case LeaveTypes.HOLIDAY: + if( dto.action === 'create'){ + // return this.holidayService.create(dto); + } else if (dto.action === 'update') { + return this.update(dto, LeaveTypes.HOLIDAY); + } else if (dto.action === 'delete'){ + return this.delete(dto, LeaveTypes.HOLIDAY); + } + case LeaveTypes.VACATION: + if( dto.action === 'create'){ + // return this.vacationService.create(dto); + } else if (dto.action === 'update') { + return this.update(dto, LeaveTypes.VACATION); + } else if (dto.action === 'delete'){ + return this.delete(dto, LeaveTypes.VACATION); + } + case LeaveTypes.SICK: + if( dto.action === 'create'){ + // return this.sickLeaveService.create(dto); + } else if (dto.action === 'update') { + return this.update(dto, LeaveTypes.SICK); + } else if (dto.action === 'delete'){ + return this.delete(dto, LeaveTypes.SICK); + } + default: + throw new BadRequestException(`Unsupported leave type: ${dto.type} or action: ${dto.action}`); + } + } -// //handle distribution to the right service according to the selected type and action -// async handle(dto: UpsertLeaveRequestDto): Promise { -// switch (dto.type) { -// case LeaveTypes.HOLIDAY: -// if( dto.action === 'create'){ -// return this.holidayLeaveService.create(dto); -// } else if (dto.action === 'update') { -// return this.update(dto, LeaveTypes.HOLIDAY); -// } else if (dto.action === 'delete'){ -// return this.delete(dto, LeaveTypes.HOLIDAY); -// } -// case LeaveTypes.VACATION: -// if( dto.action === 'create'){ -// return this.vacationLeaveService.create(dto); -// } else if (dto.action === 'update') { -// return this.update(dto, LeaveTypes.VACATION); -// } else if (dto.action === 'delete'){ -// return this.delete(dto, LeaveTypes.VACATION); -// } -// case LeaveTypes.SICK: -// if( dto.action === 'create'){ -// return this.sickLeaveService.create(dto); -// } else if (dto.action === 'update') { -// return this.update(dto, LeaveTypes.SICK); -// } else if (dto.action === 'delete'){ -// return this.delete(dto, LeaveTypes.SICK); -// } -// default: -// throw new BadRequestException(`Unsupported leave type: ${dto.type} or action: ${dto.action}`); -// } -// } + async delete(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise { + const email = dto.email.trim(); + const dates = normalizeDates(dto.dates); + const employee_id = await this.emailResolver.findIdByEmail(email); + if (!dates.length) throw new BadRequestException("Dates array must not be empty"); -// async delete(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise { -// const email = dto.email.trim(); -// const dates = normalizeDates(dto.dates); -// const employee_id = await this.emailResolver.findIdByEmail(email); -// if (!dates.length) throw new BadRequestException("Dates array must not be empty"); + const rows = await this.prisma.leaveRequests.findMany({ + where: { + employee_id: employee_id, + leave_type: type, + date: { in: dates.map((d) => toDateOnly(d)) }, + }, + select: leaveRequestsSelect, + }); -// const rows = await this.prisma.leaveRequests.findMany({ -// where: { -// employee_id: employee_id, -// leave_type: type, -// date: { in: dates.map((d) => toDateOnly(d)) }, -// }, -// select: leaveRequestsSelect, -// }); + if (rows.length !== dates.length) { + const missing = dates.filter((isoDate) => !rows.some((row) => toISODateKey(row.date) === isoDate)); + throw new NotFoundException(`No Leave request found for: ${missing.join(", ")}`); + } -// if (rows.length !== dates.length) { -// const missing = dates.filter((isoDate) => !rows.some((row) => toISODateKey(row.date) === isoDate)); -// throw new NotFoundException(`No Leave request found for: ${missing.join(", ")}`); -// } + for (const row of rows) { + if (row.approval_status === LeaveApprovalStatus.APPROVED) { + const iso = toISODateKey(row.date); + await this.leaveUtils.removeShift(email, employee_id, iso, type); + } + } -// for (const row of rows) { -// if (row.approval_status === LeaveApprovalStatus.APPROVED) { -// const iso = toISODateKey(row.date); -// await this.leaveUtils.removeShift(email, employee_id, iso, type); -// } -// } + await this.prisma.leaveRequests.deleteMany({ + where: { id: { in: rows.map((row) => row.id) } }, + }); -// await this.prisma.leaveRequests.deleteMany({ -// where: { id: { in: rows.map((row) => row.id) } }, -// }); + const deleted = rows.map((row) => ({ ...mapRowToView(row), action: "delete" as const })); + return { action: "delete", leave_requests: deleted }; + } -// const deleted = rows.map((row) => ({ ...mapRowToView(row), action: "delete" as const })); -// return { action: "delete", leave_requests: deleted }; -// } + async update(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise { + const email = dto.email.trim(); + const employee_id = await this.emailResolver.findIdByEmail(email); + const bank_code = await this.typeResolver.findByType(type); + if(!bank_code) throw new NotFoundException(`bank_code not found`); + const modifier = Number(bank_code.modifier ?? 1); + const dates = normalizeDates(dto.dates); + if (!dates.length) { + throw new BadRequestException("Dates array must not be empty"); + } -// async update(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise { -// const email = dto.email.trim(); -// const employee_id = await this.emailResolver.findIdByEmail(email); -// const bank_code = await this.typeResolver.findByType(type); -// if(!bank_code) throw new NotFoundException(`bank_code not found`); -// const modifier = Number(bank_code.modifier ?? 1); -// const dates = normalizeDates(dto.dates); -// if (!dates.length) { -// throw new BadRequestException("Dates array must not be empty"); -// } + const entries = await Promise.all( + dates.map(async (iso_date) => { + const date = toDateOnly(iso_date); + const existing = await this.prisma.leaveRequests.findUnique({ + where: { + leave_per_employee_date: { + employee_id: employee_id, + leave_type: type, + date, + }, + }, + select: leaveRequestsSelect, + }); + if (!existing) throw new NotFoundException(`No Leave request found for ${iso_date}`); + return { iso_date, date, existing }; + }), + ); -// const entries = await Promise.all( -// dates.map(async (iso_date) => { -// const date = toDateOnly(iso_date); -// const existing = await this.prisma.leaveRequests.findUnique({ -// where: { -// leave_per_employee_date: { -// employee_id: employee_id, -// leave_type: type, -// date, -// }, -// }, -// select: leaveRequestsSelect, -// }); -// if (!existing) throw new NotFoundException(`No Leave request found for ${iso_date}`); -// return { iso_date, date, existing }; -// }), -// ); + const updated: LeaveRequestViewDto[] = []; -// const updated: LeaveRequestViewDto[] = []; + if (type === LeaveTypes.SICK) { + const firstExisting = entries[0].existing; + const fallbackRequested = + firstExisting.requested_hours !== null && firstExisting.requested_hours !== undefined + ? Number(firstExisting.requested_hours) + : 8; + const requested_hours_per_day = dto.requested_hours ?? fallbackRequested; + const reference_date = entries.reduce( + (latest, entry) => (entry.date > latest ? entry.date : latest), + entries[0].date, + ); + const total_payable_hours = await this.sickLogic.calculateSickLeavePay( + employee_id, + reference_date, + entries.length, + requested_hours_per_day, + modifier, + ); + let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours)); + const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier); -// if (type === LeaveTypes.SICK) { -// const firstExisting = entries[0].existing; -// const fallbackRequested = -// firstExisting.requested_hours !== null && firstExisting.requested_hours !== undefined -// ? Number(firstExisting.requested_hours) -// : 8; -// const requested_hours_per_day = dto.requested_hours ?? fallbackRequested; -// const reference_date = entries.reduce( -// (latest, entry) => (entry.date > latest ? entry.date : latest), -// entries[0].date, -// ); -// const total_payable_hours = await this.sickLogic.calculateSickLeavePay( -// employee_id, -// reference_date, -// entries.length, -// requested_hours_per_day, -// modifier, -// ); -// let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours)); -// const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier); + for (const { iso_date, existing } of entries) { + const previous_status = existing.approval_status; + const payable = Math.min(remaining_payable_hours, daily_payable_cap); + const payable_rounded = roundToQuarterHour(Math.max(0, payable)); + remaining_payable_hours = roundToQuarterHour( + Math.max(0, remaining_payable_hours - payable_rounded), + ); -// for (const { iso_date, existing } of entries) { -// const previous_status = existing.approval_status; -// const payable = Math.min(remaining_payable_hours, daily_payable_cap); -// const payable_rounded = roundToQuarterHour(Math.max(0, payable)); -// remaining_payable_hours = roundToQuarterHour( -// Math.max(0, remaining_payable_hours - payable_rounded), -// ); + const row = await this.prisma.leaveRequests.update({ + where: { id: existing.id }, + data: { + comment: dto.comment ?? existing.comment, + requested_hours: requested_hours_per_day, + payable_hours: payable_rounded, + bank_code_id: bank_code.id, + approval_status: dto.approval_status ?? existing.approval_status, + }, + select: leaveRequestsSelect, + }); -// const row = await this.prisma.leaveRequests.update({ -// where: { id: existing.id }, -// data: { -// comment: dto.comment ?? existing.comment, -// requested_hours: requested_hours_per_day, -// payable_hours: payable_rounded, -// bank_code_id: bank_code.id, -// approval_status: dto.approval_status ?? existing.approval_status, -// }, -// select: leaveRequestsSelect, -// }); + const was_approved = previous_status === LeaveApprovalStatus.APPROVED; + const is_approved = row.approval_status === LeaveApprovalStatus.APPROVED; + const hours = Number(row.payable_hours ?? row.requested_hours ?? 0); -// const was_approved = previous_status === LeaveApprovalStatus.APPROVED; -// const is_approved = row.approval_status === LeaveApprovalStatus.APPROVED; -// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0); + if (!was_approved && is_approved) { + await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment); + } else if (was_approved && !is_approved) { + await this.leaveUtils.removeShift(email, employee_id, iso_date, type); + } else if (was_approved && is_approved) { + await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment); + } + updated.push({ ...mapRowToView(row), action: "update" }); + } + return { action: "update", leave_requests: updated }; + } -// if (!was_approved && is_approved) { -// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment); -// } else if (was_approved && !is_approved) { -// await this.leaveUtils.removeShift(email, employee_id, iso_date, type); -// } else if (was_approved && is_approved) { -// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment); -// } -// updated.push({ ...mapRowToView(row), action: "update" }); -// } -// return { action: "update", leave_requests: updated }; -// } + for (const { iso_date, date, existing } of entries) { + const previous_status = existing.approval_status; + const fallbackRequested = + existing.requested_hours !== null && existing.requested_hours !== undefined + ? Number(existing.requested_hours) + : 8; + const requested_hours = dto.requested_hours ?? fallbackRequested; -// for (const { iso_date, date, existing } of entries) { -// const previous_status = existing.approval_status; -// const fallbackRequested = -// existing.requested_hours !== null && existing.requested_hours !== undefined -// ? Number(existing.requested_hours) -// : 8; -// const requested_hours = dto.requested_hours ?? fallbackRequested; + let payable: number; + switch (type) { + case LeaveTypes.HOLIDAY: + payable = await this.holidayService.calculateHolidayPay(email, date, modifier); + break; + case LeaveTypes.VACATION: { + const days_requested = requested_hours / 8; + payable = await this.vacationLogic.calculateVacationPay( + employee_id, + date, + Math.max(0, days_requested), + modifier, + ); + break; + } + default: + payable = existing.payable_hours !== null && existing.payable_hours !== undefined + ? Number(existing.payable_hours) + : requested_hours; + } -// let payable: number; -// switch (type) { -// case LeaveTypes.HOLIDAY: -// payable = await this.holidayService.calculateHolidayPay(email, date, modifier); -// break; -// case LeaveTypes.VACATION: { -// const days_requested = requested_hours / 8; -// payable = await this.vacationLogic.calculateVacationPay( -// employee_id, -// date, -// Math.max(0, days_requested), -// modifier, -// ); -// break; -// } -// default: -// payable = existing.payable_hours !== null && existing.payable_hours !== undefined -// ? Number(existing.payable_hours) -// : requested_hours; -// } + const row = await this.prisma.leaveRequests.update({ + where: { id: existing.id }, + data: { + requested_hours, + comment: dto.comment ?? existing.comment, + payable_hours: payable, + bank_code_id: bank_code.id, + approval_status: dto.approval_status ?? existing.approval_status, + }, + select: leaveRequestsSelect, + }); -// const row = await this.prisma.leaveRequests.update({ -// where: { id: existing.id }, -// data: { -// requested_hours, -// comment: dto.comment ?? existing.comment, -// payable_hours: payable, -// bank_code_id: bank_code.id, -// approval_status: dto.approval_status ?? existing.approval_status, -// }, -// select: leaveRequestsSelect, -// }); + const was_approved = previous_status === LeaveApprovalStatus.APPROVED; + const is_approved = row.approval_status === LeaveApprovalStatus.APPROVED; + const hours = Number(row.payable_hours ?? row.requested_hours ?? 0); -// const was_approved = previous_status === LeaveApprovalStatus.APPROVED; -// const is_approved = row.approval_status === LeaveApprovalStatus.APPROVED; -// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0); - -// if (!was_approved && is_approved) { -// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment); -// } else if (was_approved && !is_approved) { -// await this.leaveUtils.removeShift(email, employee_id, iso_date, type); -// } else if (was_approved && is_approved) { -// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment); -// } -// updated.push({ ...mapRowToView(row), action: "update" }); -// } -// return { action: "update", leave_requests: updated }; -// } -// } + if (!was_approved && is_approved) { + await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment); + } else if (was_approved && !is_approved) { + await this.leaveUtils.removeShift(email, employee_id, iso_date, type); + } else if (was_approved && is_approved) { + await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment); + } + updated.push({ ...mapRowToView(row), action: "update" }); + } + return { action: "update", leave_requests: updated }; + } +} diff --git a/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts b/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts index a4554b1..145a283 100644 --- a/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts +++ b/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts @@ -1,98 +1,99 @@ -// import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto"; -// import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; -// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; -// import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; -// import { leaveRequestsSelect } from "../utils/leave-requests.select"; -// import { mapRowToView } from "../mappers/leave-requests.mapper"; -// import { PrismaService } from "src/prisma/prisma.service"; -// import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service"; -// import { roundToQuarterHour } from "src/common/utils/date-utils"; -// import { LeaveRequestsUtils } from "../utils/leave-request.util"; -// import { normalizeDates, toDateOnly } from "src/modules/shared/helpers/date-time.helpers"; -// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils"; -// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils"; +import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; +import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; +import { roundToQuarterHour } from "src/common/utils/date-utils"; +import { PrismaService } from "src/prisma/prisma.service"; +import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; +import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto"; +import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto"; +import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper"; +import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; +import { leaveRequestsSelect } from "src/time-and-attendance/modules/leave-requests/utils/leave-requests.select"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; +import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; -// @Injectable() -// export class SickLeaveRequestsService { -// constructor( -// private readonly prisma: PrismaService, -// private readonly sickService: SickLeaveService, -// private readonly leaveUtils: LeaveRequestsUtils, -// private readonly emailResolver: EmailToIdResolver, -// private readonly typeResolver: BankCodesResolver, -// ) {} -// async create(dto: UpsertLeaveRequestDto): Promise { -// const email = dto.email.trim(); -// const employee_id = await this.emailResolver.findIdByEmail(email); -// const bank_code = await this.typeResolver.findByType(LeaveTypes.SICK); -// if(!bank_code) throw new NotFoundException(`bank_code not found`); +@Injectable() +export class SickLeaveRequestsService { + constructor( + private readonly prisma: PrismaService, + private readonly sickService: SickLeaveService, + private readonly leaveUtils: LeaveRequestsUtils, + private readonly emailResolver: EmailToIdResolver, + private readonly typeResolver: BankCodesResolver, + ) {} + + async create(dto: UpsertLeaveRequestDto): Promise { + const email = dto.email.trim(); + const employee_id = await this.emailResolver.findIdByEmail(email); + const bank_code = await this.typeResolver.findByType(LeaveTypes.SICK); + if(!bank_code) throw new NotFoundException(`bank_code not found`); -// const modifier = bank_code.modifier ?? 1; -// const dates = normalizeDates(dto.dates); -// if (!dates.length) throw new BadRequestException("Dates array must not be empty"); -// const requested_hours_per_day = dto.requested_hours ?? 8; + const modifier = bank_code.modifier ?? 1; + const dates = normalizeDates(dto.dates); + if (!dates.length) throw new BadRequestException("Dates array must not be empty"); + const requested_hours_per_day = dto.requested_hours ?? 8; -// const entries = dates.map((iso) => ({ iso, date: toDateOnly(iso) })); -// const reference_date = entries.reduce( -// (latest, entry) => (entry.date > latest ? entry.date : latest), -// entries[0].date, -// ); -// const total_payable_hours = await this.sickService.calculateSickLeavePay( -// employee_id, -// reference_date, -// entries.length, -// requested_hours_per_day, -// modifier, -// ); -// let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours)); -// const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier); + const entries = dates.map((iso) => ({ iso, date: toDateOnly(iso) })); + const reference_date = entries.reduce( + (latest, entry) => (entry.date > latest ? entry.date : latest), + entries[0].date, + ); + const total_payable_hours = await this.sickService.calculateSickLeavePay( + employee_id, + reference_date, + entries.length, + requested_hours_per_day, + modifier, + ); + let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours)); + const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier); -// const created: LeaveRequestViewDto[] = []; + const created: LeaveRequestViewDto[] = []; -// for (const { iso, date } of entries) { -// const existing = await this.prisma.leaveRequests.findUnique({ -// where: { -// leave_per_employee_date: { -// employee_id: employee_id, -// leave_type: LeaveTypes.SICK, -// date, -// }, -// }, -// select: { id: true }, -// }); -// if (existing) { -// throw new BadRequestException(`Sick request already exists for ${iso}`); -// } + for (const { iso, date } of entries) { + const existing = await this.prisma.leaveRequests.findUnique({ + where: { + leave_per_employee_date: { + employee_id: employee_id, + leave_type: LeaveTypes.SICK, + date, + }, + }, + select: { id: true }, + }); + if (existing) { + throw new BadRequestException(`Sick request already exists for ${iso}`); + } -// const payable = Math.min(remaining_payable_hours, daily_payable_cap); -// const payable_rounded = roundToQuarterHour(Math.max(0, payable)); -// remaining_payable_hours = roundToQuarterHour( -// Math.max(0, remaining_payable_hours - payable_rounded), -// ); + const payable = Math.min(remaining_payable_hours, daily_payable_cap); + const payable_rounded = roundToQuarterHour(Math.max(0, payable)); + remaining_payable_hours = roundToQuarterHour( + Math.max(0, remaining_payable_hours - payable_rounded), + ); -// const row = await this.prisma.leaveRequests.create({ -// data: { -// employee_id: employee_id, -// bank_code_id: bank_code.id, -// leave_type: LeaveTypes.SICK, -// comment: dto.comment ?? "", -// requested_hours: requested_hours_per_day, -// payable_hours: payable_rounded, -// approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING, -// date, -// }, -// select: leaveRequestsSelect, -// }); + const row = await this.prisma.leaveRequests.create({ + data: { + employee_id: employee_id, + bank_code_id: bank_code.id, + leave_type: LeaveTypes.SICK, + comment: dto.comment ?? "", + requested_hours: requested_hours_per_day, + payable_hours: payable_rounded, + approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING, + date, + }, + select: leaveRequestsSelect, + }); -// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0); -// if (row.approval_status === LeaveApprovalStatus.APPROVED) { -// await this.leaveUtils.syncShift(email, employee_id, iso, hours,LeaveTypes.SICK, row.comment); -// } + const hours = Number(row.payable_hours ?? row.requested_hours ?? 0); + if (row.approval_status === LeaveApprovalStatus.APPROVED) { + // await this.leaveUtils.syncShift(email, employee_id, iso, hours,LeaveTypes.SICK, row.comment); + } -// created.push({ ...mapRowToView(row), action: "create" }); -// } + created.push({ ...mapRowToView(row), action: "create" }); + } -// return { action: "create", leave_requests: created }; -// } -// } + return { action: "create", leave_requests: created }; + } +} diff --git a/src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts b/src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts index 34223f5..652feff 100644 --- a/src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts +++ b/src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts @@ -1,93 +1,93 @@ - -// import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto"; -// import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; -// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; -// import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; -// import { VacationService } from "src/modules/business-logics/services/vacation.service"; -// import { PrismaService } from "src/prisma/prisma.service"; -// import { mapRowToView } from "../mappers/leave-requests.mapper"; -// import { leaveRequestsSelect } from "../utils/leave-requests.select"; -// import { roundToQuarterHour } from "src/common/utils/date-utils"; -// import { LeaveRequestsUtils } from "../utils/leave-request.util"; -// import { normalizeDates, toDateOnly } from "src/modules/shared/helpers/date-time.helpers"; -// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils"; -// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils"; +import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; +import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; +import { roundToQuarterHour } from "src/common/utils/date-utils"; +import { PrismaService } from "src/prisma/prisma.service"; +import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; +import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto"; +import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto"; +import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper"; +import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; +import { leaveRequestsSelect } from "src/time-and-attendance/modules/leave-requests/utils/leave-requests.select"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; +import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; -// @Injectable() -// export class VacationLeaveRequestsService { -// constructor( -// private readonly prisma: PrismaService, -// private readonly vacationService: VacationService, -// private readonly leaveUtils: LeaveRequestsUtils, -// private readonly emailResolver: EmailToIdResolver, -// private readonly typeResolver: BankCodesResolver, -// ) {} -// async create(dto: UpsertLeaveRequestDto): Promise { -// const email = dto.email.trim(); -// const employee_id = await this.emailResolver.findIdByEmail(email); -// const bank_code = await this.typeResolver.findByType(LeaveTypes.VACATION); -// if(!bank_code) throw new NotFoundException(`bank_code not found`); +@Injectable() +export class VacationLeaveRequestsService { + constructor( + private readonly prisma: PrismaService, + private readonly vacationService: VacationService, + private readonly leaveUtils: LeaveRequestsUtils, + private readonly emailResolver: EmailToIdResolver, + private readonly typeResolver: BankCodesResolver, + ) {} + + async create(dto: UpsertLeaveRequestDto): Promise { + const email = dto.email.trim(); + const employee_id = await this.emailResolver.findIdByEmail(email); + const bank_code = await this.typeResolver.findByType(LeaveTypes.VACATION); + if(!bank_code) throw new NotFoundException(`bank_code not found`); -// const modifier = bank_code.modifier ?? 1; -// const dates = normalizeDates(dto.dates); -// const requested_hours_per_day = dto.requested_hours ?? 8; -// if (!dates.length) throw new BadRequestException("Dates array must not be empty"); + const modifier = bank_code.modifier ?? 1; + const dates = normalizeDates(dto.dates); + const requested_hours_per_day = dto.requested_hours ?? 8; + if (!dates.length) throw new BadRequestException("Dates array must not be empty"); -// const entries = dates -// .map((iso) => ({ iso, date: toDateOnly(iso) })) -// .sort((a, b) => a.date.getTime() - b.date.getTime()); -// const start_date = entries[0].date; -// const total_payable_hours = await this.vacationService.calculateVacationPay( -// employee_id, -// start_date, -// entries.length, -// modifier, -// ); -// let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours)); -// const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier); + const entries = dates + .map((iso) => ({ iso, date: toDateOnly(iso) })) + .sort((a, b) => a.date.getTime() - b.date.getTime()); + const start_date = entries[0].date; + const total_payable_hours = await this.vacationService.calculateVacationPay( + employee_id, + start_date, + entries.length, + modifier, + ); + let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours)); + const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier); -// const created: LeaveRequestViewDto[] = []; + const created: LeaveRequestViewDto[] = []; -// for (const { iso, date } of entries) { -// const existing = await this.prisma.leaveRequests.findUnique({ -// where: { -// leave_per_employee_date: { -// employee_id: employee_id, -// leave_type: LeaveTypes.VACATION, -// date, -// }, -// }, -// select: { id: true }, -// }); -// if (existing) throw new BadRequestException(`Vacation request already exists for ${iso}`); + for (const { iso, date } of entries) { + const existing = await this.prisma.leaveRequests.findUnique({ + where: { + leave_per_employee_date: { + employee_id: employee_id, + leave_type: LeaveTypes.VACATION, + date, + }, + }, + select: { id: true }, + }); + if (existing) throw new BadRequestException(`Vacation request already exists for ${iso}`); -// const payable = Math.min(remaining_payable_hours, daily_payable_cap); -// const payable_rounded = roundToQuarterHour(Math.max(0, payable)); -// remaining_payable_hours = roundToQuarterHour( -// Math.max(0, remaining_payable_hours - payable_rounded), -// ); + const payable = Math.min(remaining_payable_hours, daily_payable_cap); + const payable_rounded = roundToQuarterHour(Math.max(0, payable)); + remaining_payable_hours = roundToQuarterHour( + Math.max(0, remaining_payable_hours - payable_rounded), + ); -// const row = await this.prisma.leaveRequests.create({ -// data: { -// employee_id: employee_id, -// bank_code_id: bank_code.id, -// payable_hours: payable_rounded, -// requested_hours: requested_hours_per_day, -// leave_type: LeaveTypes.VACATION, -// comment: dto.comment ?? "", -// approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING, -// date, -// }, -// select: leaveRequestsSelect, -// }); + const row = await this.prisma.leaveRequests.create({ + data: { + employee_id: employee_id, + bank_code_id: bank_code.id, + payable_hours: payable_rounded, + requested_hours: requested_hours_per_day, + leave_type: LeaveTypes.VACATION, + comment: dto.comment ?? "", + approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING, + date, + }, + select: leaveRequestsSelect, + }); -// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0); -// if (row.approval_status === LeaveApprovalStatus.APPROVED) { -// await this.leaveUtils.syncShift(email, employee_id, iso, hours, LeaveTypes.VACATION, row.comment); -// } -// created.push({ ...mapRowToView(row), action: "create" }); -// } -// return { action: "create", leave_requests: created }; -// } -// } + const hours = Number(row.payable_hours ?? row.requested_hours ?? 0); + if (row.approval_status === LeaveApprovalStatus.APPROVED) { + // await this.leaveUtils.syncShift(email, employee_id, iso, hours, LeaveTypes.VACATION, row.comment); + } + created.push({ ...mapRowToView(row), action: "create" }); + } + return { action: "create", leave_requests: created }; + } +} diff --git a/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts b/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts index d01ccf2..3b40236 100644 --- a/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts +++ b/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts @@ -1,104 +1,106 @@ -// import { hhmmFromLocal, toDateOnly, toStringFromDate } from "src/modules/shared/helpers/date-time.helpers"; -// import { BadRequestException, Injectable } from "@nestjs/common"; -// import { PrismaService } from "src/prisma/prisma.service"; -// import { LeaveTypes } from "@prisma/client"; -// import { UpsertAction } from "src/modules/shared/types/upsert-actions.types"; -// @Injectable() -// export class LeaveRequestsUtils { -// constructor( -// private readonly prisma: PrismaService, -// private readonly shiftsCommand: ShiftsCommandService, -// ){} +import { BadRequestException, Injectable } from "@nestjs/common"; +import { PrismaService } from "src/prisma/prisma.service"; +import { LeaveTypes } from "@prisma/client"; +import { toDateOnly, toStringFromDate, hhmmFromLocal } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; +import { UpsertAction } from "src/time-and-attendance/modules/shared/types/upsert-actions.types"; +import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; -// async syncShift( -// email: string, -// employee_id: number, -// date: string, -// hours: number, -// type: LeaveTypes, -// comment?: string, -// ) { -// if (hours <= 0) return; +@Injectable() +export class LeaveRequestsUtils { + constructor( + private readonly prisma: PrismaService, + private readonly shiftsService: ShiftsUpsertService, + ){} -// const duration_minutes = Math.round(hours * 60); -// if (duration_minutes > 8 * 60) { -// throw new BadRequestException("Amount of hours cannot exceed 8 hours per day."); -// } -// const date_only = toDateOnly(date); -// const yyyy_mm_dd = toStringFromDate(date_only); + async syncShift( + email: string, + employee_id: number, + date: string, + hours: number, + type: LeaveTypes, + comment?: string, + ) { + if (hours <= 0) return; + + const duration_minutes = Math.round(hours * 60); + if (duration_minutes > 8 * 60) { + throw new BadRequestException("Amount of hours cannot exceed 8 hours per day."); + } + const date_only = toDateOnly(date); + const yyyy_mm_dd = toStringFromDate(date_only); -// const start_minutes = 8 * 60; -// const end_minutes = start_minutes + duration_minutes; -// const toHHmm = (total: number) => -// `${String(Math.floor(total / 60)).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`; + const start_minutes = 8 * 60; + const end_minutes = start_minutes + duration_minutes; + const toHHmm = (total: number) => + `${String(Math.floor(total / 60)).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`; -// const existing = await this.prisma.shifts.findFirst({ -// where: { -// date: date_only, -// bank_code: { type }, -// timesheet: { employee_id: employee_id }, -// }, -// include: { bank_code: true }, -// }); + const existing = await this.prisma.shifts.findFirst({ + where: { + date: date_only, + bank_code: { type }, + timesheet: { employee_id: employee_id }, + }, + include: { bank_code: true }, + }); -// const action: UpsertAction = existing ? 'update' : 'create'; + const action: UpsertAction = existing ? 'update' : 'create'; -// await this.shiftsCommand.upsertShifts(email, action, { -// old_shift: existing -// ? { -// date: yyyy_mm_dd, -// start_time: existing.start_time.toISOString().slice(11, 16), -// end_time: existing.end_time.toISOString().slice(11, 16), -// type: existing.bank_code?.type ?? type, -// is_remote: existing.is_remote, -// is_approved:existing.is_approved, -// comment: existing.comment ?? undefined, -// } -// : undefined, -// new_shift: { -// date: yyyy_mm_dd, -// start_time: toHHmm(start_minutes), -// end_time: toHHmm(end_minutes), -// is_remote: existing?.is_remote ?? false, -// is_approved:existing?.is_approved ?? false, -// comment: comment ?? existing?.comment ?? "", -// type: type, -// }, -// }); -// } + // await this.shiftsService.upsertShifts(email, action, { + // old_shift: existing + // ? { + // date: yyyy_mm_dd, + // start_time: existing.start_time.toISOString().slice(11, 16), + // end_time: existing.end_time.toISOString().slice(11, 16), + // type: existing.bank_code?.type ?? type, + // is_remote: existing.is_remote, + // is_approved:existing.is_approved, + // comment: existing.comment ?? undefined, + // } + // : undefined, + // new_shift: { + // date: yyyy_mm_dd, + // start_time: toHHmm(start_minutes), + // end_time: toHHmm(end_minutes), + // is_remote: existing?.is_remote ?? false, + // is_approved:existing?.is_approved ?? false, + // comment: comment ?? existing?.comment ?? "", + // type: type, + // }, + // }); + } -// async removeShift( -// email: string, -// employee_id: number, -// iso_date: string, -// type: LeaveTypes, -// ) { -// const date_only = toDateOnly(iso_date); -// const yyyy_mm_dd = toStringFromDate(date_only); -// const existing = await this.prisma.shifts.findFirst({ -// where: { -// date: date_only, -// bank_code: { type }, -// timesheet: { employee_id: employee_id }, -// }, -// include: { bank_code: true }, -// }); -// if (!existing) return; + async removeShift( + email: string, + employee_id: number, + iso_date: string, + type: LeaveTypes, + ) { + const date_only = toDateOnly(iso_date); + const yyyy_mm_dd = toStringFromDate(date_only); + const existing = await this.prisma.shifts.findFirst({ + where: { + date: date_only, + bank_code: { type }, + timesheet: { employee_id: employee_id }, + }, + include: { bank_code: true }, + }); + if (!existing) return; -// await this.shiftsCommand.upsertShifts(email, 'delete', { -// old_shift: { -// date: yyyy_mm_dd, -// start_time: hhmmFromLocal(existing.start_time), -// end_time: hhmmFromLocal(existing.end_time), -// type: existing.bank_code?.type ?? type, -// is_remote: existing.is_remote, -// is_approved:existing.is_approved, -// comment: existing.comment ?? undefined, -// }, -// }); -// } + // await this.shiftsService.upsertShifts(email, 'delete', { + // old_shift: { + // date: yyyy_mm_dd, + // start_time: hhmmFromLocal(existing.start_time), + // end_time: hhmmFromLocal(existing.end_time), + // type: existing.bank_code?.type ?? type, + // is_remote: existing.is_remote, + // is_approved:existing.is_approved, + // comment: existing.comment ?? undefined, + // }, + // }); + } -// } +} diff --git a/src/time-and-attendance/time-and-attendance.module.ts b/src/time-and-attendance/time-and-attendance.module.ts index ebcbd05..e3eff36 100644 --- a/src/time-and-attendance/time-and-attendance.module.ts +++ b/src/time-and-attendance/time-and-attendance.module.ts @@ -1,20 +1,20 @@ -import { Module } from "@nestjs/common"; -import { ExpensesArchiveController } from "src/modules/archival/controllers/expenses-archive.controller"; -import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; -import { ExpenseController } from "src/time-and-attendance/modules/expenses/controllers/expense.controller"; -import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; -import { ExpensesArchivalService } from "src/time-and-attendance/modules/expenses/services/expenses-archival.service"; -import { PayperiodsModule } from "src/time-and-attendance/modules/pay-period/pay-periods.module"; -import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; -import { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller"; import { SchedulePresetsCommandService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service"; +import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service"; import { SchedulePresetsQueryService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service"; -import { ShiftController } from "src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller"; -import { ShiftsGetService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service"; +import { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller"; +import { ExpensesArchivalService } from "src/time-and-attendance/modules/expenses/services/expenses-archival.service"; +// import { LeaveRequestController } from "src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller"; +import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; +import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; import { TimesheetController } from "src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller"; -import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service"; +import { ExpenseController } from "src/time-and-attendance/modules/expenses/controllers/expense.controller"; +import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { PayperiodsModule } from "src/time-and-attendance/modules/pay-period/pay-periods.module"; +import { ShiftsGetService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service"; +import { ShiftController } from "src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller"; +import { Module } from "@nestjs/common"; @Module({ imports: [BusinessLogicsModule, PayperiodsModule], @@ -23,7 +23,6 @@ import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/ti ShiftController, SchedulePresetsController, ExpenseController, - ExpensesArchiveController, // LeaveRequestController, ], From 3ae1085e95a1de9b816026bc2a2a212235a97489 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 27 Oct 2025 10:17:11 -0400 Subject: [PATCH 07/57] fix(schema): temporary comment on some schema lines to prep for removing employees/customer from this module --- prisma/schema.prisma | 62 ++++++++++++++++++++++---------------------- src/app.module.ts | 7 ----- 2 files changed, 31 insertions(+), 38 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index fd3b522..f00155e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -14,44 +14,44 @@ datasource db { url = env("DATABASE_URL_DEV") } -// model Users { -// id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid -// first_name String -// last_name String -// email String @unique -// phone_number String @unique -// residence String? -// role Roles @default(GUEST) +model Users { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + first_name String + last_name String + email String @unique + phone_number String @unique + residence String? + role Roles @default(GUEST) -// employee Employees? @relation("UserEmployee") -// oauth_sessions OAuthSessions[] @relation("UserOAuthSessions") -// preferences Preferences? @relation("UserPreferences") + employee Employees? @relation("UserEmployee") + // oauth_sessions OAuthSessions[] @relation("UserOAuthSessions") + // preferences Preferences? @relation("UserPreferences") -// @@map("users") -// } + @@map("users") +} -// model Employees { -// id Int @id @default(autoincrement()) -// user Users @relation("UserEmployee", fields: [user_id], references: [id]) -// user_id String @unique @db.Uuid -// supervisor Employees? @relation("EmployeeSupervisor", fields: [supervisor_id], references: [id]) -// supervisor_id Int? +model Employees { + id Int @id @default(autoincrement()) + user Users @relation("UserEmployee", fields: [user_id], references: [id]) + user_id String @unique @db.Uuid + supervisor Employees? @relation("EmployeeSupervisor", fields: [supervisor_id], references: [id]) + supervisor_id Int? -// external_payroll_id Int -// company_code Int -// first_work_day DateTime @db.Date -// last_work_day DateTime? @db.Date -// job_title String? -// is_supervisor Boolean @default(false) + external_payroll_id Int + company_code Int + first_work_day DateTime @db.Date + last_work_day DateTime? @db.Date + job_title String? + is_supervisor Boolean @default(false) -// crew Employees[] @relation("EmployeeSupervisor") -// timesheet Timesheets[] @relation("TimesheetEmployee") -// leave_request LeaveRequests[] @relation("LeaveRequestEmployee") -// schedule_presets SchedulePresets[] @relation("SchedulePreset") + crew Employees[] @relation("EmployeeSupervisor") + // timesheet Timesheets[] @relation("TimesheetEmployee") + // leave_request LeaveRequests[] @relation("LeaveRequestEmployee") + // schedule_presets SchedulePresets[] @relation("SchedulePreset") -// @@map("employees") -// } + @@map("employees") +} model LeaveRequests { id Int @id @default(autoincrement()) diff --git a/src/app.module.ts b/src/app.module.ts index c8035c4..91f6af6 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,9 +1,7 @@ import { BadRequestException, Module, ValidationPipe } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; -// import { ArchivalModule } from './modules/archival/archival.module'; import { AuthenticationModule } from './identity-and-account/authentication/auth.module'; -// import { BankCodesModule } from './modules/bank-codes/bank-codes.module'; // import { CsvExportModule } from './modules/exports/csv-exports.module'; import { HealthModule } from './health/health.module'; import { HealthController } from './health/health.controller'; @@ -23,11 +21,8 @@ import { TimeAndAttendanceModule } from 'src/time-and-attendance/time-and-attend @Module({ imports: [ AuthenticationModule, - // BankCodesModule, ConfigModule.forRoot({isGlobal: true}), // CsvExportModule, - // CustomersModule, - // EmployeesModule, HealthModule, NotificationsModule, OauthSessionsModule, @@ -35,9 +30,7 @@ import { TimeAndAttendanceModule } from 'src/time-and-attendance/time-and-attend PreferencesModule, PrismaModule, ScheduleModule.forRoot(), //cronjobs - // ShiftsModule, TimeAndAttendanceModule, - // TimesheetsModule, UsersModule, ], controllers: [AppController, HealthController], From ac1eaabb5d35a16573c424860ed431e772488498 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 27 Oct 2025 10:20:56 -0400 Subject: [PATCH 08/57] fix(schema): remove commented lines --- docs/swagger/swagger-spec.json | 302 --------------------------------- prisma/schema.prisma | 20 +-- 2 files changed, 10 insertions(+), 312 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 85ce3df..5369ee3 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -99,200 +99,6 @@ ] } }, - "/oauth-sessions": { - "post": { - "operationId": "OauthSessionsController_create", - "parameters": [], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateOauthSessionDto" - } - } - } - }, - "responses": { - "201": { - "description": "OAuth session created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateOauthSessionDto" - } - } - } - }, - "400": { - "description": "Incomplete task or invalid data" - } - }, - "security": [ - { - "sessions": [] - } - ], - "summary": "Create OAuth session", - "tags": [ - "OAuth Sessions" - ] - }, - "get": { - "operationId": "OauthSessionsController_findAll", - "parameters": [], - "responses": { - "201": { - "description": "List of OAuth session found", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CreateOauthSessionDto" - } - } - } - } - }, - "400": { - "description": "List of OAuth session not found" - } - }, - "security": [ - { - "sessions": [] - } - ], - "summary": "Find all OAuth session", - "tags": [ - "OAuth Sessions" - ] - } - }, - "/oauth-sessions/{id}": { - "get": { - "operationId": "OauthSessionsController_findOne", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - } - ], - "responses": { - "201": { - "description": "OAuth session found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateOauthSessionDto" - } - } - } - }, - "400": { - "description": "OAuth session not found" - } - }, - "security": [ - { - "sessions": [] - } - ], - "summary": "Find OAuth session", - "tags": [ - "OAuth Sessions" - ] - }, - "patch": { - "operationId": "OauthSessionsController_update", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateOauthSessionDto" - } - } - } - }, - "responses": { - "201": { - "description": "OAuth session updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateOauthSessionDto" - } - } - } - }, - "400": { - "description": "OAuth session not found" - } - }, - "security": [ - { - "sessions": [] - } - ], - "summary": "Update OAuth session", - "tags": [ - "OAuth Sessions" - ] - }, - "delete": { - "operationId": "OauthSessionsController_remove", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - } - ], - "responses": { - "201": { - "description": "OAuth session deleted", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateOauthSessionDto" - } - } - } - }, - "400": { - "description": "OAuth session not found" - } - }, - "security": [ - { - "sessions": [] - } - ], - "summary": "Delete OAuth session", - "tags": [ - "OAuth Sessions" - ] - } - }, "/pay-periods/current-and-all": { "get": { "operationId": "PayPeriodsController_getCurrentAndAll", @@ -887,114 +693,6 @@ } }, "schemas": { - "CreateOauthSessionDto": { - "type": "object", - "properties": { - "id": { - "type": "string", - "example": "cklwi0vb70000z2z20q6f19qk", - "description": "Unique ID of an OAuth token (auto-generated)" - }, - "user_id": { - "type": "string", - "example": "S7A2U8R7O6N6", - "description": "User`s unique identification number" - }, - "application": { - "type": "string", - "example": "app.targo.ca", - "description": "URL in which the access token is used for" - }, - "access_token": { - "type": "string", - "example": "L5O6R4D3/O6F3#T8H4E3&R6I4N6G4S7 ...", - "description": "Access token" - }, - "refresh_token": { - "type": "string", - "example": "Th3731102h1p07Th3R1n92", - "description": "Refresh token" - }, - "access_token_expiry": { - "format": "date-time", - "type": "string", - "example": "25/12/3018", - "description": "Access token`s expiry date" - }, - "refresh_token_expiry": { - "format": "date-time", - "type": "string", - "example": "26/02/3019", - "description": "Refresh token`s expiry date" - }, - "scopes": { - "example": "access tolkiens, email, etc... ", - "description": "scopes of infos linked to the access token", - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "id", - "user_id", - "application", - "access_token", - "refresh_token", - "access_token_expiry" - ] - }, - "UpdateOauthSessionDto": { - "type": "object", - "properties": { - "id": { - "type": "string", - "example": "cklwi0vb70000z2z20q6f19qk", - "description": "Unique ID of an OAuth token (auto-generated)" - }, - "user_id": { - "type": "string", - "example": "S7A2U8R7O6N6", - "description": "User`s unique identification number" - }, - "application": { - "type": "string", - "example": "app.targo.ca", - "description": "URL in which the access token is used for" - }, - "access_token": { - "type": "string", - "example": "L5O6R4D3/O6F3#T8H4E3&R6I4N6G4S7 ...", - "description": "Access token" - }, - "refresh_token": { - "type": "string", - "example": "Th3731102h1p07Th3R1n92", - "description": "Refresh token" - }, - "access_token_expiry": { - "format": "date-time", - "type": "string", - "example": "25/12/3018", - "description": "Access token`s expiry date" - }, - "refresh_token_expiry": { - "format": "date-time", - "type": "string", - "example": "26/02/3019", - "description": "Refresh token`s expiry date" - }, - "scopes": { - "example": "access tolkiens, email, etc... ", - "description": "scopes of infos linked to the access token", - "type": "array", - "items": { - "type": "string" - } - } - } - }, "PayPeriodDto": { "type": "object", "properties": { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f00155e..60503eb 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -24,8 +24,8 @@ model Users { role Roles @default(GUEST) employee Employees? @relation("UserEmployee") - // oauth_sessions OAuthSessions[] @relation("UserOAuthSessions") - // preferences Preferences? @relation("UserPreferences") + oauth_sessions OAuthSessions[] @relation("UserOAuthSessions") + preferences Preferences? @relation("UserPreferences") @@map("users") } @@ -46,16 +46,16 @@ model Employees { crew Employees[] @relation("EmployeeSupervisor") - // timesheet Timesheets[] @relation("TimesheetEmployee") - // leave_request LeaveRequests[] @relation("LeaveRequestEmployee") - // schedule_presets SchedulePresets[] @relation("SchedulePreset") + timesheet Timesheets[] @relation("TimesheetEmployee") + leave_request LeaveRequests[] @relation("LeaveRequestEmployee") + schedule_presets SchedulePresets[] @relation("SchedulePreset") @@map("employees") } model LeaveRequests { id Int @id @default(autoincrement()) - // employee Employees @relation("LeaveRequestEmployee", fields: [employee_id], references: [id]) + employee Employees @relation("LeaveRequestEmployee", fields: [employee_id], references: [id]) employee_id Int bank_code BankCodes @relation("LeaveRequestBankCodes", fields: [bank_code_id], references: [id]) bank_code_id Int @@ -107,7 +107,7 @@ view PayPeriods { model Timesheets { id Int @id @default(autoincrement()) - // employee Employees @relation("TimesheetEmployee", fields: [employee_id], references: [id]) + employee Employees @relation("TimesheetEmployee", fields: [employee_id], references: [id]) employee_id Int start_date DateTime @db.Date @@ -135,7 +135,7 @@ model TimesheetsArchive { model SchedulePresets { id Int @id @default(autoincrement()) - // employee Employees @relation("SchedulePreset", fields: [employee_id], references: [id]) + employee Employees @relation("SchedulePreset", fields: [employee_id], references: [id]) employee_id Int name String @@ -258,7 +258,7 @@ model ExpensesArchive { model OAuthSessions { id String @id @default(cuid()) - // user Users @relation("UserOAuthSessions", fields: [user_id], references: [id]) + user Users @relation("UserOAuthSessions", fields: [user_id], references: [id]) user_id String @db.Uuid application String access_token String @unique @@ -327,7 +327,7 @@ model AttachmentVariants { model Preferences { id Int @id @default(autoincrement()) - // user Users @relation("UserPreferences", fields: [user_id], references: [id]) + user Users @relation("UserPreferences", fields: [user_id], references: [id]) user_id String @unique @db.Uuid notifications Int @default(0) From 5e49bc5df67dbc306f72988766add56fd840cc45 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 27 Oct 2025 11:47:38 -0400 Subject: [PATCH 09/57] refactor(utils): moved utils to shared utils folder --- docs/swagger/swagger-spec.json | 24 ++---- src/app.controller.ts | 10 +-- src/app.service.ts | 6 +- .../controllers/preferences.controller.ts | 11 ++- .../preferences/preferences.module.ts | 4 +- .../services/preferences.service.ts | 17 ++-- .../domains/business-logics.module.ts | 12 +-- .../domains/services/after-hours.service.ts | 79 ------------------- .../controllers/expense.controller.ts | 12 +-- .../{expense.dto.ts => expense-create.dto.ts} | 0 ...{get-expense.dto.ts => expense-get.dto.ts} | 0 ...e-expense.dto.ts => expense-update.dto.ts} | 2 +- .../modules/expenses/expenses.module.ts | 4 - .../helpers/expenses-date-time-helpers.ts | 5 -- .../services/expense-upsert.service.ts | 55 ++++++------- .../leave-requests/leave-requests.module.ts | 18 ++--- .../mappers/leave-requests.mapper.ts | 2 +- .../holiday-leave-requests.service.ts | 16 ++-- .../services/leave-request.service.ts | 4 +- .../services/sick-leave-requests.service.ts | 3 +- .../vacation-leave-requests.service.ts | 4 +- .../utils/leave-request.transform.ts | 2 +- .../utils/leave-request.util.ts | 11 +-- .../modules/pay-period/pay-periods.module.ts | 5 -- .../modules/pay-period/utils/pay-year.util.ts | 16 ++-- .../shared/constants/date-time.constant.ts | 2 - .../shared/constants/regex.constant.ts | 2 - .../shared/constants/utils.constant.ts | 1 - .../shared/helpers/date-time.helpers.ts | 4 +- .../shared/utils/resolve-timesheet.utils.ts | 2 +- .../shifts/controllers/shift.controller.ts | 20 +---- .../{shift.dto.ts => shift-create.dto.ts} | 0 .../{get-shift.dto.ts => shift-get.dto.ts} | 0 ...pdate-shift.dto.ts => shift-update.dto.ts} | 2 +- .../services/shifts-archival.service.ts | 13 +++ .../shifts/services/shifts-get.service.ts | 15 +++- .../shifts/services/shifts-upsert.service.ts | 8 +- .../time-tracker/shifts/shifts.module.ts | 17 +--- .../date-time-helpers.ts} | 8 +- .../selects.utils.ts} | 18 ++++- .../after-hours.service.ts | 79 +++++++++++++++++++ .../create-expense.dto.ts | 0 .../create-timesheet.dto.ts | 0 .../date-time.constant.ts | 2 + .../expenses-command.service.ts | 0 .../expenses-query.service.ts | 0 .../expenses.controller.ts | 0 .../expenses.types.interfaces.ts | 0 .../~misc_deprecated-files/expenses.utils.ts | 0 .../get-shift-overview.dto.ts | 0 .../leave-requests.select.ts | 23 ++++++ src/~misc_deprecated-files/regex.constant.ts | 2 + .../search-expense.dto.ts | 0 .../search-timesheet.dto.ts | 0 .../shifts-command.service.ts | 0 .../shifts-overview-row.interface.ts | 0 .../shifts-query.service.ts | 0 .../shifts-upsert.types.ts | 0 .../shifts.controller.ts | 0 .../~misc_deprecated-files/shifts.helpers.ts | 0 .../~misc_deprecated-files/shifts.utils.ts | 0 .../timesheet-period.dto.ts | 0 .../timesheet.helpers.ts | 0 .../timesheet.mappers.ts | 0 .../timesheet.selectors.ts | 0 .../~misc_deprecated-files/timesheet.types.ts | 0 .../~misc_deprecated-files/timesheet.utils.ts | 0 .../timesheets-command.service.ts | 0 .../timesheets-query.service.ts | 0 .../timesheets.controller.ts | 0 .../update-expense.dto.ts | 0 .../upsert-expense.dto.ts | 0 .../upsert-shift.dto.ts | 0 src/~misc_deprecated-files/utils.constant.ts | 1 + 74 files changed, 259 insertions(+), 282 deletions(-) delete mode 100644 src/time-and-attendance/domains/services/after-hours.service.ts rename src/time-and-attendance/modules/expenses/dtos/{expense.dto.ts => expense-create.dto.ts} (100%) rename src/time-and-attendance/modules/expenses/dtos/{get-expense.dto.ts => expense-get.dto.ts} (100%) rename src/time-and-attendance/modules/expenses/dtos/{update-expense.dto.ts => expense-update.dto.ts} (77%) delete mode 100644 src/time-and-attendance/modules/expenses/helpers/expenses-date-time-helpers.ts delete mode 100644 src/time-and-attendance/modules/shared/constants/date-time.constant.ts delete mode 100644 src/time-and-attendance/modules/shared/constants/regex.constant.ts delete mode 100644 src/time-and-attendance/modules/shared/constants/utils.constant.ts rename src/time-and-attendance/modules/time-tracker/shifts/dtos/{shift.dto.ts => shift-create.dto.ts} (100%) rename src/time-and-attendance/modules/time-tracker/shifts/dtos/{get-shift.dto.ts => shift-get.dto.ts} (100%) rename src/time-and-attendance/modules/time-tracker/shifts/dtos/{update-shift.dto.ts => shift-update.dto.ts} (84%) rename src/time-and-attendance/{modules/time-tracker/shifts/helpers/shifts-date-time-helpers.ts => utils/date-time-helpers.ts} (80%) rename src/time-and-attendance/{modules/leave-requests/utils/leave-requests.select.ts => utils/selects.utils.ts} (65%) create mode 100644 src/~misc_deprecated-files/after-hours.service.ts rename src/{time-and-attendance/modules/expenses => }/~misc_deprecated-files/create-expense.dto.ts (100%) rename src/{time-and-attendance/modules/time-tracker/timesheets => }/~misc_deprecated-files/create-timesheet.dto.ts (100%) create mode 100644 src/~misc_deprecated-files/date-time.constant.ts rename src/{time-and-attendance/modules/expenses => }/~misc_deprecated-files/expenses-command.service.ts (100%) rename src/{time-and-attendance/modules/expenses => }/~misc_deprecated-files/expenses-query.service.ts (100%) rename src/{time-and-attendance/modules/expenses => }/~misc_deprecated-files/expenses.controller.ts (100%) rename src/{time-and-attendance/modules/expenses => }/~misc_deprecated-files/expenses.types.interfaces.ts (100%) rename src/{time-and-attendance/modules/expenses => }/~misc_deprecated-files/expenses.utils.ts (100%) rename src/{time-and-attendance/modules/time-tracker/shifts => }/~misc_deprecated-files/get-shift-overview.dto.ts (100%) create mode 100644 src/~misc_deprecated-files/leave-requests.select.ts create mode 100644 src/~misc_deprecated-files/regex.constant.ts rename src/{time-and-attendance/modules/expenses => }/~misc_deprecated-files/search-expense.dto.ts (100%) rename src/{time-and-attendance/modules/time-tracker/timesheets => }/~misc_deprecated-files/search-timesheet.dto.ts (100%) rename src/{time-and-attendance/modules/time-tracker/shifts => }/~misc_deprecated-files/shifts-command.service.ts (100%) rename src/{time-and-attendance/modules/time-tracker/shifts => }/~misc_deprecated-files/shifts-overview-row.interface.ts (100%) rename src/{time-and-attendance/modules/time-tracker/shifts => }/~misc_deprecated-files/shifts-query.service.ts (100%) rename src/{time-and-attendance/modules/time-tracker/shifts => }/~misc_deprecated-files/shifts-upsert.types.ts (100%) rename src/{time-and-attendance/modules/time-tracker/shifts => }/~misc_deprecated-files/shifts.controller.ts (100%) rename src/{time-and-attendance/modules/time-tracker/shifts => }/~misc_deprecated-files/shifts.helpers.ts (100%) rename src/{time-and-attendance/modules/time-tracker/shifts => }/~misc_deprecated-files/shifts.utils.ts (100%) rename src/{time-and-attendance/modules/time-tracker/timesheets => }/~misc_deprecated-files/timesheet-period.dto.ts (100%) rename src/{time-and-attendance/modules/time-tracker/timesheets => }/~misc_deprecated-files/timesheet.helpers.ts (100%) rename src/{time-and-attendance/modules/time-tracker/timesheets => }/~misc_deprecated-files/timesheet.mappers.ts (100%) rename src/{time-and-attendance/modules/time-tracker/timesheets => }/~misc_deprecated-files/timesheet.selectors.ts (100%) rename src/{time-and-attendance/modules/time-tracker/timesheets => }/~misc_deprecated-files/timesheet.types.ts (100%) rename src/{time-and-attendance/modules/time-tracker/timesheets => }/~misc_deprecated-files/timesheet.utils.ts (100%) rename src/{time-and-attendance/modules/time-tracker/timesheets => }/~misc_deprecated-files/timesheets-command.service.ts (100%) rename src/{time-and-attendance/modules/time-tracker/timesheets => }/~misc_deprecated-files/timesheets-query.service.ts (100%) rename src/{time-and-attendance/modules/time-tracker/timesheets => }/~misc_deprecated-files/timesheets.controller.ts (100%) rename src/{time-and-attendance/modules/expenses => }/~misc_deprecated-files/update-expense.dto.ts (100%) rename src/{time-and-attendance/modules/expenses => }/~misc_deprecated-files/upsert-expense.dto.ts (100%) rename src/{time-and-attendance/modules/time-tracker/shifts => }/~misc_deprecated-files/upsert-shift.dto.ts (100%) create mode 100644 src/~misc_deprecated-files/utils.constant.ts diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 5369ee3..1ca3c19 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -1,20 +1,6 @@ { "openapi": "3.0.0", "paths": { - "/": { - "get": { - "operationId": "AppController_getHello", - "parameters": [], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "App" - ] - } - }, "/health": { "get": { "operationId": "HealthController_check", @@ -319,16 +305,16 @@ ] } }, - "/preferences/{email}": { + "/preferences": { "patch": { "operationId": "PreferencesController_updatePreferences", "parameters": [ { - "name": "email", + "name": "PreferencesDto", "required": true, - "in": "path", + "in": "body", "schema": { - "type": "string" + "$ref": "#/components/schemas/PreferencesDto" } } ], @@ -337,7 +323,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PreferencesDto" + "type": "number" } } } diff --git a/src/app.controller.ts b/src/app.controller.ts index cce879e..0aa298e 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,12 +1,4 @@ import { Controller, Get } from '@nestjs/common'; -import { AppService } from './app.service'; @Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get() - getHello(): string { - return this.appService.getHello(); - } -} +export class AppController { } diff --git a/src/app.service.ts b/src/app.service.ts index 927d7cc..c45bea9 100644 --- a/src/app.service.ts +++ b/src/app.service.ts @@ -1,8 +1,4 @@ import { Injectable } from '@nestjs/common'; @Injectable() -export class AppService { - getHello(): string { - return 'Hello World!'; - } -} +export class AppService { } diff --git a/src/identity-and-account/preferences/controllers/preferences.controller.ts b/src/identity-and-account/preferences/controllers/preferences.controller.ts index ae5af16..a185837 100644 --- a/src/identity-and-account/preferences/controllers/preferences.controller.ts +++ b/src/identity-and-account/preferences/controllers/preferences.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Param, Patch } from "@nestjs/common"; +import { Body, Controller, Patch } from "@nestjs/common"; import { PreferencesService } from "../services/preferences.service"; import { PreferencesDto } from "../dtos/preferences.dto"; @@ -6,9 +6,12 @@ import { PreferencesDto } from "../dtos/preferences.dto"; export class PreferencesController { constructor(private readonly service: PreferencesService){} - @Patch(':email') - async updatePreferences(@Param('email') email: string, @Body()payload: PreferencesDto) { - return this.service.updatePreferences(email, payload); + @Patch() + async updatePreferences( + @Body() user_id: number, + @Body() payload: PreferencesDto + ) { + return this.service.updatePreferences(user_id, payload); } } \ No newline at end of file diff --git a/src/identity-and-account/preferences/preferences.module.ts b/src/identity-and-account/preferences/preferences.module.ts index 65deb82..1873933 100644 --- a/src/identity-and-account/preferences/preferences.module.ts +++ b/src/identity-and-account/preferences/preferences.module.ts @@ -1,10 +1,8 @@ -import { Module } from "@nestjs/common"; import { PreferencesController } from "./controllers/preferences.controller"; import { PreferencesService } from "./services/preferences.service"; -import { SharedModule } from "../../time-and-attendance/modules/shared/shared.module"; +import { Module } from "@nestjs/common"; @Module({ - imports: [SharedModule], controllers: [ PreferencesController ], providers: [ PreferencesService ], exports: [ PreferencesService ], diff --git a/src/identity-and-account/preferences/services/preferences.service.ts b/src/identity-and-account/preferences/services/preferences.service.ts index 2ff02ac..4e3c108 100644 --- a/src/identity-and-account/preferences/services/preferences.service.ts +++ b/src/identity-and-account/preferences/services/preferences.service.ts @@ -1,20 +1,15 @@ -import { Injectable } from "@nestjs/common"; -import { Preferences } from "@prisma/client"; -import { PrismaService } from "src/prisma/prisma.service"; import { PreferencesDto } from "../dtos/preferences.dto"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { PrismaService } from "src/prisma/prisma.service"; +import { Preferences } from "@prisma/client"; +import { Injectable } from "@nestjs/common"; @Injectable() export class PreferencesService { - constructor( - private readonly prisma: PrismaService, - private readonly emailResolver: EmailToIdResolver , - ){} + constructor( private readonly prisma: PrismaService ){} - async updatePreferences(email: string, dto: PreferencesDto ): Promise { - const user_id = await this.emailResolver.resolveUserIdWithEmail(email); + async updatePreferences(user_id: number, dto: PreferencesDto ): Promise { return this.prisma.preferences.update({ - where: { user_id }, + where: { id: user_id }, data: { notifications: dto.notifications, dark_mode: dto.dark_mode, diff --git a/src/time-and-attendance/domains/business-logics.module.ts b/src/time-and-attendance/domains/business-logics.module.ts index f84330d..5b103b8 100644 --- a/src/time-and-attendance/domains/business-logics.module.ts +++ b/src/time-and-attendance/domains/business-logics.module.ts @@ -1,18 +1,15 @@ -import { Module } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; -//import { AfterHoursService } from "./services/after-hours.service"; -import { HolidayService } from "./services/holiday.service"; -import { OvertimeService } from "./services/overtime.service"; import { SickLeaveService } from "./services/sick-leave.service"; +import { OvertimeService } from "./services/overtime.service"; import { VacationService } from "./services/vacation.service"; +import { HolidayService } from "./services/holiday.service"; import { MileageService } from "./services/mileage.service"; +import { PrismaService } from "src/prisma/prisma.service"; +import { Module } from "@nestjs/common"; -//AfterHours is not used, need to clarify infos before implementing into shifts.service @Module({ providers: [ PrismaService, - //AfterHoursService, HolidayService, MileageService, OvertimeService, @@ -20,7 +17,6 @@ import { MileageService } from "./services/mileage.service"; VacationService ], exports: [ - //AfterHoursService, HolidayService, MileageService, OvertimeService, diff --git a/src/time-and-attendance/domains/services/after-hours.service.ts b/src/time-and-attendance/domains/services/after-hours.service.ts deleted file mode 100644 index 2330b33..0000000 --- a/src/time-and-attendance/domains/services/after-hours.service.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { BadRequestException, Injectable, Logger } from "@nestjs/common"; -import { PrismaService } from "../../../prisma/prisma.service"; - - -//THIS SERVICE IS NOT USED, RULES TO BE DETERMINED WITH MIKE/HR/ACCOUNTING -@Injectable() -export class AfterHoursService { - private readonly logger = new Logger(AfterHoursService.name); - private static readonly BUSINESS_START = 7; - private static readonly BUSINESS_END = 18; - private static readonly ROUND_MINUTES = 15; - - constructor(private readonly prisma: PrismaService) {} - - - private getPreBusinessMinutes(start: Date, end: Date): number { - const biz_start = new Date(start); - biz_start.setHours(AfterHoursService.BUSINESS_START, 0,0,0); - - if (end>= start || start >= biz_start) { - return 0; - } - - const segment_end = end < biz_start ? end : biz_start; - const minutes = (segment_end.getTime() - start.getTime()) / 60000; - - this.logger.debug(`getPreBusinessMintutes -> ${minutes.toFixed(1)}min`); - return minutes; - - } - - private getPostBusinessMinutes(start: Date, end: Date): number { - const biz_end = new Date(start); - biz_end.setHours(AfterHoursService.BUSINESS_END,0,0,0); - - if( end <= biz_end ) { - return 0; - } - - const segment_start = start > biz_end ? start : biz_end; - const minutes = (end.getTime() - segment_start.getTime()) / 60000; - - this.logger.debug(`getPostBusinessMintutes -> ${minutes.toFixed(1)}min`); - return minutes; - - } - - private roundToNearestQUarterMinute(minutes: number): number { - const rounded = Math.round(minutes / AfterHoursService.ROUND_MINUTES) - * AfterHoursService.ROUND_MINUTES; - this.logger.debug(`roundToNearestQuarterMinute -> raw=${minutes.toFixed(1)}min, rounded= ${rounded}min`); - return rounded; - } - - public computeAfterHours(start: Date, end:Date): number { - if(end.getTime() <= start.getTime()) { - throw new BadRequestException('The end cannot be before the starting of the shift'); - } - - if (start.toDateString() !== end.toDateString()) { - throw new BadRequestException('you cannot enter a shift that start in a day and end in the next' + - 'You must create 2 instances, one on the first day and the second during the next day.'); - } - - const pre_min = this.getPreBusinessMinutes(start, end); - const post_min = this.getPostBusinessMinutes(start, end); - const raw_aftermin = pre_min + post_min; - - const rounded_min = this.roundToNearestQUarterMinute(raw_aftermin); - - const hours = rounded_min / 60; - const result = parseFloat(hours.toFixed(2)); - - this.logger.debug(`computeAfterHours -> raw_aftermin = ${raw_aftermin.toFixed(1)}min, + - rounded = ${rounded_min}min, hours = ${result.toFixed(2)}`); - return result; - } -} - diff --git a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts b/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts index b7a3736..47dca6b 100644 --- a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts @@ -1,16 +1,12 @@ -import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; -import { ExpenseDto } from "../dtos/expense.dto"; +import { Body, Controller, Delete, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; import { CreateResult, ExpenseUpsertService, UpdateResult } from "../services/expense-upsert.service"; -import { updateExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/update-expense.dto"; +import { updateExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-update.dto"; +import { ExpenseDto } from "../dtos/expense-create.dto"; @Controller('expense') export class ExpenseController { - constructor( - private readonly prisma: PrismaService, - private readonly upsert_service: ExpenseUpsertService, - ){} + constructor( private readonly upsert_service: ExpenseUpsertService ){} @Post(':timesheet_id') create( diff --git a/src/time-and-attendance/modules/expenses/dtos/expense.dto.ts b/src/time-and-attendance/modules/expenses/dtos/expense-create.dto.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/dtos/expense.dto.ts rename to src/time-and-attendance/modules/expenses/dtos/expense-create.dto.ts diff --git a/src/time-and-attendance/modules/expenses/dtos/get-expense.dto.ts b/src/time-and-attendance/modules/expenses/dtos/expense-get.dto.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/dtos/get-expense.dto.ts rename to src/time-and-attendance/modules/expenses/dtos/expense-get.dto.ts diff --git a/src/time-and-attendance/modules/expenses/dtos/update-expense.dto.ts b/src/time-and-attendance/modules/expenses/dtos/expense-update.dto.ts similarity index 77% rename from src/time-and-attendance/modules/expenses/dtos/update-expense.dto.ts rename to src/time-and-attendance/modules/expenses/dtos/expense-update.dto.ts index fc709bb..6108682 100644 --- a/src/time-and-attendance/modules/expenses/dtos/update-expense.dto.ts +++ b/src/time-and-attendance/modules/expenses/dtos/expense-update.dto.ts @@ -1,5 +1,5 @@ import { OmitType, PartialType } from "@nestjs/swagger"; -import { ExpenseDto } from "./expense.dto"; +import { ExpenseDto } from "./expense-create.dto"; export class updateExpenseDto extends PartialType ( OmitType(ExpenseDto, ['is_approved', 'timesheet_id'] as const) diff --git a/src/time-and-attendance/modules/expenses/expenses.module.ts b/src/time-and-attendance/modules/expenses/expenses.module.ts index 923ce3e..17da55a 100644 --- a/src/time-and-attendance/modules/expenses/expenses.module.ts +++ b/src/time-and-attendance/modules/expenses/expenses.module.ts @@ -2,14 +2,10 @@ import { ExpensesArchivalService } from "./services/expenses-archival.service"; import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; import { ExpenseController } from "src/time-and-attendance/modules/expenses/controllers/expense.controller"; import { Module } from "@nestjs/common"; -import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; -import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; @Module({ - imports: [ BusinessLogicsModule, SharedModule ], controllers: [ ExpenseController ], providers: [ ExpenseUpsertService, ExpensesArchivalService ], - exports: [ ExpensesArchivalService ], }) export class ExpensesModule {} \ No newline at end of file diff --git a/src/time-and-attendance/modules/expenses/helpers/expenses-date-time-helpers.ts b/src/time-and-attendance/modules/expenses/helpers/expenses-date-time-helpers.ts deleted file mode 100644 index ef81852..0000000 --- a/src/time-and-attendance/modules/expenses/helpers/expenses-date-time-helpers.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const toDateFromString = (ymd: string): Date => { - return new Date(`${ymd}T00:00:00:000Z`); -} -export const toStringFromDate = (date: Date) => - date.toISOString().slice(0,10); \ No newline at end of file diff --git a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts index d2a332c..bc35988 100644 --- a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts @@ -1,9 +1,11 @@ -import { toDateFromString, toStringFromDate } from "../helpers/expenses-date-time-helpers"; + +import { toDateFromString, toStringFromDate } from "src/time-and-attendance/utils/date-time-helpers"; import { Injectable, NotFoundException } from "@nestjs/common"; -import { updateExpenseDto } from "../dtos/update-expense.dto"; -import { GetExpenseDto } from "../dtos/get-expense.dto"; +import { updateExpenseDto } from "../dtos/expense-update.dto"; +import { GetExpenseDto } from "../dtos/expense-get.dto"; import { PrismaService } from "src/prisma/prisma.service"; -import { ExpenseDto } from "../dtos/expense.dto"; +import { ExpenseDto } from "../dtos/expense-create.dto"; +import { Prisma } from "@prisma/client"; type Normalized = { date: Date; comment: string; supervisor_comment?: string; }; @@ -43,18 +45,7 @@ export class ExpenseUpsertService { is_approved: dto.is_approved, }, //return the newly created expense with id - select: { - id: true, - timesheet_id: true, - bank_code_id: true, - attachment: true, - date: true, - amount: true, - mileage: true, - comment: true, - supervisor_comment: true, - is_approved: true, - }, + select: expense_select, }); //build an object to return to the frontend to display @@ -103,18 +94,7 @@ export class ExpenseUpsertService { const expense = await this.prisma.expenses.update({ where: { id }, data, - select: { - id: true, - timesheet_id: true, - bank_code_id: true, - attachment: true, - date: true, - amount: true, - mileage: true, - comment: true, - supervisor_comment: true, - is_approved: true, - }, + select: expense_select, }); const updated: GetExpenseDto = { @@ -181,4 +161,21 @@ export class ExpenseUpsertService { if (Number.isNaN(parsed)) throw new Error(`Invalid value : ${value} for ${field}`); return parsed; }; -} \ No newline at end of file + + +} + + + + export const expense_select = { + id: true, + timesheet_id: true, + bank_code_id: true, + attachment: true, + date: true, + amount: true, + mileage: true, + comment: true, + supervisor_comment: true, + is_approved: true, + } satisfies Prisma.ExpensesSelect; \ No newline at end of file diff --git a/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts b/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts index 954f07a..25fc768 100644 --- a/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts +++ b/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts @@ -1,18 +1,17 @@ -import { Module } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; -import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; -import { HolidayService } from "src/time-and-attendance/domains/services/holiday.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 { LeaveRequestController } from "src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller"; import { LeaveRequestsService } from "src/time-and-attendance/modules/leave-requests/services/leave-request.service"; +import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; -import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; +import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; +import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; +import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; +import { PrismaService } from "src/prisma/prisma.service"; import { ShiftsModule } from "src/time-and-attendance/modules/time-tracker/shifts/shifts.module"; +import { Module } from "@nestjs/common"; @Module({ - imports: [BusinessLogicsModule, ShiftsModule, SharedModule], + imports: [BusinessLogicsModule, ShiftsModule ], controllers: [LeaveRequestController], providers: [ VacationService, @@ -22,9 +21,6 @@ import { ShiftsModule } from "src/time-and-attendance/modules/time-tracker/shift PrismaService, LeaveRequestsUtils, ], - exports: [ - LeaveRequestsService, - ], }) export class LeaveRequestsModule {} \ No newline at end of file diff --git a/src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper.ts b/src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper.ts index e93f94b..48d1214 100644 --- a/src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper.ts +++ b/src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper.ts @@ -1,6 +1,6 @@ import { Prisma } from "@prisma/client"; import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; -import { LeaveRequestRow } from "../utils/leave-requests.select"; +import { LeaveRequestRow } from "src/time-and-attendance/utils/selects.utils"; const toNum = (value?: Prisma.Decimal | null) => value !== null && value !== undefined ? Number(value) : undefined; diff --git a/src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts b/src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts index 43f4716..148f4bf 100644 --- a/src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts +++ b/src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts @@ -1,15 +1,15 @@ import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; -import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; -import { PrismaService } from "src/prisma/prisma.service"; -import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; -import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto"; import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto"; -import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper"; -import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; -import { leaveRequestsSelect } from "src/time-and-attendance/modules/leave-requests/utils/leave-requests.select"; +import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; +import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto"; +import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; +import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; +import { PrismaService } from "src/prisma/prisma.service"; +import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper"; @Injectable() @@ -26,8 +26,8 @@ export class HolidayLeaveRequestsService { const email = dto.email.trim(); const employee_id = await this.emailResolver.findIdByEmail(email); const bank_code = await this.typeResolver.findByType(LeaveTypes.HOLIDAY); - if(!bank_code) throw new NotFoundException(`bank_code not found`); const dates = normalizeDates(dto.dates); + if (!bank_code) throw new NotFoundException(`bank_code not found`); if (!dates.length) throw new BadRequestException('Dates array must not be empty'); const created: LeaveRequestViewDto[] = []; diff --git a/src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts b/src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts index 8b44ac0..0fd0ceb 100644 --- a/src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts +++ b/src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts @@ -4,7 +4,6 @@ import { roundToQuarterHour } from "src/common/utils/date-utils"; import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto"; import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; import { mapRowToView } from "../mappers/leave-requests.mapper"; -import { leaveRequestsSelect } from "../utils/leave-requests.select"; import { PrismaService } from "src/prisma/prisma.service"; import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; @@ -13,14 +12,13 @@ import { normalizeDates, toDateOnly, toISODateKey } from "src/time-and-attendanc import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; +import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; @Injectable() export class LeaveRequestsService { constructor( private readonly prisma: PrismaService, private readonly holidayService: HolidayService, private readonly sickLogic: SickLeaveService, - private readonly sickLeaveService: SickLeaveService, - private readonly vacationService: VacationService, private readonly vacationLogic: VacationService, private readonly leaveUtils: LeaveRequestsUtils, private readonly emailResolver: EmailToIdResolver, diff --git a/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts b/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts index 145a283..13783b2 100644 --- a/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts +++ b/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts @@ -7,10 +7,10 @@ import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-reque import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto"; import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper"; import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; -import { leaveRequestsSelect } from "src/time-and-attendance/modules/leave-requests/utils/leave-requests.select"; import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; @Injectable() @@ -18,7 +18,6 @@ export class SickLeaveRequestsService { constructor( private readonly prisma: PrismaService, private readonly sickService: SickLeaveService, - private readonly leaveUtils: LeaveRequestsUtils, private readonly emailResolver: EmailToIdResolver, private readonly typeResolver: BankCodesResolver, ) {} diff --git a/src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts b/src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts index 652feff..9f0afc7 100644 --- a/src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts +++ b/src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts @@ -6,11 +6,10 @@ import { VacationService } from "src/time-and-attendance/domains/services/vacati import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto"; import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto"; import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper"; -import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; -import { leaveRequestsSelect } from "src/time-and-attendance/modules/leave-requests/utils/leave-requests.select"; import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; @Injectable() @@ -18,7 +17,6 @@ export class VacationLeaveRequestsService { constructor( private readonly prisma: PrismaService, private readonly vacationService: VacationService, - private readonly leaveUtils: LeaveRequestsUtils, private readonly emailResolver: EmailToIdResolver, private readonly typeResolver: BankCodesResolver, ) {} diff --git a/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts b/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts index 63b9936..f5501c0 100644 --- a/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts +++ b/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts @@ -1,8 +1,8 @@ +import { LeaveRequestRow } from 'src/time-and-attendance/utils/selects.utils'; import { LeaveRequestViewDto } from '../dtos/leave-request-view.dto'; import { mapArchiveRowToView } from '../mappers/leave-requests-archive.mapper'; import { mapRowToView } from '../mappers/leave-requests.mapper'; import { LeaveRequestArchiveRow } from './leave-requests-archive.select'; -import { LeaveRequestRow } from './leave-requests.select'; /** Active (table leave_requests) : proxy to base mapper */ export function mapRowToViewWithDays(row: LeaveRequestRow): LeaveRequestViewDto { diff --git a/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts b/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts index 3b40236..776c56e 100644 --- a/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts +++ b/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts @@ -2,15 +2,16 @@ import { BadRequestException, Injectable } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { LeaveTypes } from "@prisma/client"; -import { toDateOnly, toStringFromDate, hhmmFromLocal } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; +// import { toDateOnly, toStringFromDate } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; import { UpsertAction } from "src/time-and-attendance/modules/shared/types/upsert-actions.types"; -import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; +import { toDateFromString, toStringFromDate } from "src/time-and-attendance/utils/date-time-helpers"; +// import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; @Injectable() export class LeaveRequestsUtils { constructor( private readonly prisma: PrismaService, - private readonly shiftsService: ShiftsUpsertService, + // private readonly shiftsService: ShiftsUpsertService, ){} async syncShift( @@ -27,7 +28,7 @@ export class LeaveRequestsUtils { if (duration_minutes > 8 * 60) { throw new BadRequestException("Amount of hours cannot exceed 8 hours per day."); } - const date_only = toDateOnly(date); + const date_only = toDateFromString(date); const yyyy_mm_dd = toStringFromDate(date_only); @@ -78,7 +79,7 @@ export class LeaveRequestsUtils { iso_date: string, type: LeaveTypes, ) { - const date_only = toDateOnly(iso_date); + const date_only = toDateFromString(iso_date); const yyyy_mm_dd = toStringFromDate(date_only); const existing = await this.prisma.shifts.findFirst({ where: { diff --git a/src/time-and-attendance/modules/pay-period/pay-periods.module.ts b/src/time-and-attendance/modules/pay-period/pay-periods.module.ts index 6e15221..2a26c5a 100644 --- a/src/time-and-attendance/modules/pay-period/pay-periods.module.ts +++ b/src/time-and-attendance/modules/pay-period/pay-periods.module.ts @@ -1,19 +1,14 @@ import { PayPeriodsQueryService } from "./services/pay-periods-query.service"; -import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; import { PayPeriodsController } from "./controllers/pay-periods.controller"; import { PrismaService } from "src/prisma/prisma.service"; -import { PrismaModule } from "src/prisma/prisma.module"; -import { SharedModule } from "../shared/shared.module"; import { Module } from "@nestjs/common"; @Module({ - imports: [PrismaModule, SharedModule, BusinessLogicsModule], providers: [ PayPeriodsQueryService, PrismaService, ], controllers: [PayPeriodsController], - exports: [ PayPeriodsQueryService ], }) export class PayperiodsModule {} \ No newline at end of file diff --git a/src/time-and-attendance/modules/pay-period/utils/pay-year.util.ts b/src/time-and-attendance/modules/pay-period/utils/pay-year.util.ts index dd9a512..1dbf0d0 100644 --- a/src/time-and-attendance/modules/pay-period/utils/pay-year.util.ts +++ b/src/time-and-attendance/modules/pay-period/utils/pay-year.util.ts @@ -1,14 +1,10 @@ +import { toStringFromDate, toUTCDate } from "src/time-and-attendance/utils/date-time-helpers"; + export const ANCHOR_ISO = '2023-12-17'; // ancre date const PERIOD_DAYS = 14; const PERIODS_PER_YEAR = 26; const MS_PER_DAY = 86_400_000; -const toUTCDate = (iso: string | Date) => { - const d = typeof iso === 'string' ? new Date(iso + 'T00:00:00.000Z') : iso; - return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())); -}; -export const toDateString = (d: Date) => d.toISOString().slice(0, 10); - export function payYearOfDate(date: string | Date, anchorISO = ANCHOR_ISO): number { const ANCHOR = toUTCDate(anchorISO); const d = toUTCDate(date); @@ -27,10 +23,10 @@ export function computePeriod(pay_year: number, period_no: number, anchorISO = A return { period_no: period_no, pay_year: pay_year, - payday: toDateString(pay), - period_start: toDateString(start), - period_end: toDateString(end), - label: `${toDateString(start)}.${toDateString(end)}`, + payday: toStringFromDate(pay), + period_start: toStringFromDate(start), + period_end: toStringFromDate(end), + label: `${toStringFromDate(start)}.${toStringFromDate(end)}`, start, end, }; } diff --git a/src/time-and-attendance/modules/shared/constants/date-time.constant.ts b/src/time-and-attendance/modules/shared/constants/date-time.constant.ts deleted file mode 100644 index 9cf4f96..0000000 --- a/src/time-and-attendance/modules/shared/constants/date-time.constant.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const MS_PER_DAY = 86_400_000; -export const MS_PER_HOUR = 3_600_000; \ No newline at end of file diff --git a/src/time-and-attendance/modules/shared/constants/regex.constant.ts b/src/time-and-attendance/modules/shared/constants/regex.constant.ts deleted file mode 100644 index 30f77c1..0000000 --- a/src/time-and-attendance/modules/shared/constants/regex.constant.ts +++ /dev/null @@ -1,2 +0,0 @@ -const HH_MM_REGEX = /^([01]\d|2[0-3]):[0-5]\d$/; -const DATE_ISO_FORMAT = /^\d{4}-\d{2}-\d{2}$/; \ No newline at end of file diff --git a/src/time-and-attendance/modules/shared/constants/utils.constant.ts b/src/time-and-attendance/modules/shared/constants/utils.constant.ts deleted file mode 100644 index 271bbbf..0000000 --- a/src/time-and-attendance/modules/shared/constants/utils.constant.ts +++ /dev/null @@ -1 +0,0 @@ -export const COMMENT_MAX_LENGTH = 280; \ No newline at end of file diff --git a/src/time-and-attendance/modules/shared/helpers/date-time.helpers.ts b/src/time-and-attendance/modules/shared/helpers/date-time.helpers.ts index 6716321..2076530 100644 --- a/src/time-and-attendance/modules/shared/helpers/date-time.helpers.ts +++ b/src/time-and-attendance/modules/shared/helpers/date-time.helpers.ts @@ -15,8 +15,8 @@ export const toDateOnly = (s: string): Date => { return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate(), 0,0,0,0); }; -export const toStringFromDate = (d: Date) => - `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`; +// export const toStringFromDate = (d: Date) => +// `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`; export const toISOtoDateOnly = (iso: string): Date => { diff --git a/src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts b/src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts index 11c89f2..a992af8 100644 --- a/src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts +++ b/src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts @@ -1,6 +1,6 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { Prisma, PrismaClient } from "@prisma/client"; -import { weekStartSunday } from "src/time-and-attendance/modules/time-tracker/shifts/helpers/shifts-date-time-helpers"; +import { weekStartSunday } from "src/time-and-attendance/utils/date-time-helpers"; import { PrismaService } from "src/prisma/prisma.service"; import { EmailToIdResolver } from "./resolve-email-id.utils"; diff --git a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts index b35766d..6f0358f 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts @@ -1,23 +1,12 @@ -import { BadRequestException, Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from "@nestjs/common"; +import { BadRequestException, Body, Controller, Delete, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; import { CreateResult, ShiftsUpsertService, UpdateResult } from "../services/shifts-upsert.service"; -import { updateShiftDto } from "../dtos/update-shift.dto"; -import { ShiftDto } from "../dtos/shift.dto"; -import { ShiftsGetService } from "../services/shifts-get.service"; +import { updateShiftDto } from "../dtos/shift-update.dto"; +import { ShiftDto } from "../dtos/shift-create.dto"; @Controller('shift') export class ShiftController { - constructor( - private readonly upsert_service: ShiftsUpsertService, - private readonly get_service: ShiftsGetService - ){} - - // @Get() - // async getShiftsByIds( - // @Query("shift_ids") shift_ids: string) { - // const parsed = shift_ids.split(/,\s*/).map(value => Number(value)).filter(Number.isFinite); - // return this.get_service.getShiftByShiftId(parsed); - // } + constructor( private readonly upsert_service: ShiftsUpsertService ){} @Post(':timesheet_id') createBatch( @@ -25,7 +14,6 @@ export class ShiftController { @Body()dtos: ShiftDto[]): Promise { const list = Array.isArray(dtos) ? dtos : []; if(list.length === 0) throw new BadRequestException('Body is missing or invalid (create shifts)') - return this.upsert_service.createShifts(timesheet_id, dtos) } diff --git a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift.dto.ts b/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/dtos/shift.dto.ts rename to src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/dtos/get-shift.dto.ts b/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/dtos/get-shift.dto.ts rename to src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/dtos/update-shift.dto.ts b/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts similarity index 84% rename from src/time-and-attendance/modules/time-tracker/shifts/dtos/update-shift.dto.ts rename to src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts index ebbbd13..95ef660 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/dtos/update-shift.dto.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts @@ -1,5 +1,5 @@ import { PartialType, OmitType } from "@nestjs/swagger"; -import { ShiftDto } from "./shift.dto"; +import { ShiftDto } from "./shift-create.dto"; export class updateShiftDto extends PartialType ( //allows update using ShiftDto and preventing OmitType variables to be modified diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts index 667ba3a..9a6671a 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts @@ -1,6 +1,19 @@ +import { Injectable } from "@nestjs/common"; import { ShiftsArchive } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; + +/** + * _____________________________________________________________________________________ + * + * + * This service is not used. Will be use to atrchive a list of shifts using a cron job. + * + * + * _____________________________________________________________________________________ + */ + +@Injectable() export class ShiftsArchivalService { constructor(private readonly prisma: PrismaService){} diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts index 1a79f49..5d17686 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts @@ -1,7 +1,18 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; -import { GetShiftDto } from "../dtos/get-shift.dto"; -import { toStringFromDate, toStringFromHHmm } from "../helpers/shifts-date-time-helpers"; +import { GetShiftDto } from "../dtos/shift-get.dto"; +import { toStringFromDate, toStringFromHHmm } from "../../../../utils/date-time-helpers"; + +/** + * _____________________________________________________________________________________ + * + * + * This service is not used. Could be use to show a list of shifts. + * + * For the moment, the module Timesheets is used to display shifts, filtered by employee + * + * _____________________________________________________________________________________ + */ @Injectable() export class ShiftsGetService { diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts index a7ec458..6049359 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts @@ -1,10 +1,10 @@ -import { toDateFromString, toHHmmFromString, toStringFromDate, toStringFromHHmm } from "../helpers/shifts-date-time-helpers"; +import { toDateFromString, toHHmmFromString, toStringFromDate, toStringFromHHmm } from "../../../../utils/date-time-helpers"; import { BadRequestException, ConflictException, Injectable, NotFoundException } from "@nestjs/common"; import { OvertimeService, WeekOvertimeSummary } from "src/time-and-attendance/domains/services/overtime.service"; -import { updateShiftDto } from "../dtos/update-shift.dto"; +import { updateShiftDto } from "../dtos/shift-update.dto"; import { PrismaService } from "src/prisma/prisma.service"; -import { GetShiftDto } from "../dtos/get-shift.dto"; -import { ShiftDto } from "../dtos/shift.dto"; +import { GetShiftDto } from "../dtos/shift-get.dto"; +import { ShiftDto } from "../dtos/shift-create.dto"; type Normalized = { date: Date; start_time: Date; end_time: Date; }; diff --git a/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts b/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts index 00fe148..2669e7d 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts @@ -1,24 +1,13 @@ -import { ShiftsArchivalService } from './services/shifts-archival.service'; import { BusinessLogicsModule } from 'src/time-and-attendance/domains/business-logics.module'; -import { NotificationsModule } from '../../../../modules/notifications/notifications.module'; import { ShiftsUpsertService } from './services/shifts-upsert.service'; import { ShiftsGetService } from './services/shifts-get.service'; import { ShiftController } from './controllers/shift.controller'; -import { SharedModule } from '../../shared/shared.module'; import { Module } from '@nestjs/common'; @Module({ - imports: [ - BusinessLogicsModule, - NotificationsModule, - SharedModule, - ], + imports: [ BusinessLogicsModule ], controllers: [ShiftController], - providers: [ - ShiftsArchivalService, - ShiftsGetService, - ShiftsUpsertService, - ], - exports: [ ShiftsUpsertService, ShiftsGetService ], + providers: [ ShiftsGetService, ShiftsUpsertService ], + exports: [ ShiftsUpsertService ], }) export class ShiftsModule {} diff --git a/src/time-and-attendance/modules/time-tracker/shifts/helpers/shifts-date-time-helpers.ts b/src/time-and-attendance/utils/date-time-helpers.ts similarity index 80% rename from src/time-and-attendance/modules/time-tracker/shifts/helpers/shifts-date-time-helpers.ts rename to src/time-and-attendance/utils/date-time-helpers.ts index 1ed0854..504a41b 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/helpers/shifts-date-time-helpers.ts +++ b/src/time-and-attendance/utils/date-time-helpers.ts @@ -1,3 +1,4 @@ +//ensures the week starts from sunday export function weekStartSunday(date_local: Date): Date { const start = new Date(Date.UTC(date_local.getFullYear(), date_local.getMonth(), date_local.getDate())); const dow = start.getDay(); // 0 = dimanche @@ -28,4 +29,9 @@ export const toHHmmFromString = (hhmm: string): Date => { //converts Date format to string export const toDateFromString = (ymd: string): Date => { return new Date(`${ymd}T00:00:00:000Z`); -} \ No newline at end of file +} + +export const toUTCDate = (iso: string | Date) => { + const d = typeof iso === 'string' ? new Date(iso + 'T00:00:00.000Z') : iso; + return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())); +}; \ No newline at end of file diff --git a/src/time-and-attendance/modules/leave-requests/utils/leave-requests.select.ts b/src/time-and-attendance/utils/selects.utils.ts similarity index 65% rename from src/time-and-attendance/modules/leave-requests/utils/leave-requests.select.ts rename to src/time-and-attendance/utils/selects.utils.ts index e48a930..ac5845f 100644 --- a/src/time-and-attendance/modules/leave-requests/utils/leave-requests.select.ts +++ b/src/time-and-attendance/utils/selects.utils.ts @@ -1,6 +1,20 @@ import { Prisma } from "@prisma/client"; -//custom prisma select to avoid employee_id exposure + +export const expense_select = { + id: true, + timesheet_id: true, + bank_code_id: true, + attachment: true, + date: true, + amount: true, + mileage: true, + comment: true, + supervisor_comment: true, + is_approved: true, +} satisfies Prisma.ExpensesSelect; + + export const leaveRequestsSelect = { id: true, bank_code_id: true, @@ -20,4 +34,4 @@ export const leaveRequestsSelect = { }}, } satisfies Prisma.LeaveRequestsSelect; -export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>; +export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>; \ No newline at end of file diff --git a/src/~misc_deprecated-files/after-hours.service.ts b/src/~misc_deprecated-files/after-hours.service.ts new file mode 100644 index 0000000..c5e3425 --- /dev/null +++ b/src/~misc_deprecated-files/after-hours.service.ts @@ -0,0 +1,79 @@ +// import { BadRequestException, Injectable, Logger } from "@nestjs/common"; +// import { PrismaService } from "../prisma/prisma.service"; + + +// //THIS SERVICE IS NOT USED, RULES TO BE DETERMINED WITH MIKE/HR/ACCOUNTING +// @Injectable() +// export class AfterHoursService { +// private readonly logger = new Logger(AfterHoursService.name); +// private static readonly BUSINESS_START = 7; +// private static readonly BUSINESS_END = 18; +// private static readonly ROUND_MINUTES = 15; + +// constructor(private readonly prisma: PrismaService) {} + + +// private getPreBusinessMinutes(start: Date, end: Date): number { +// const biz_start = new Date(start); +// biz_start.setHours(AfterHoursService.BUSINESS_START, 0,0,0); + +// if (end>= start || start >= biz_start) { +// return 0; +// } + +// const segment_end = end < biz_start ? end : biz_start; +// const minutes = (segment_end.getTime() - start.getTime()) / 60000; + +// this.logger.debug(`getPreBusinessMintutes -> ${minutes.toFixed(1)}min`); +// return minutes; + +// } + +// private getPostBusinessMinutes(start: Date, end: Date): number { +// const biz_end = new Date(start); +// biz_end.setHours(AfterHoursService.BUSINESS_END,0,0,0); + +// if( end <= biz_end ) { +// return 0; +// } + +// const segment_start = start > biz_end ? start : biz_end; +// const minutes = (end.getTime() - segment_start.getTime()) / 60000; + +// this.logger.debug(`getPostBusinessMintutes -> ${minutes.toFixed(1)}min`); +// return minutes; + +// } + +// private roundToNearestQUarterMinute(minutes: number): number { +// const rounded = Math.round(minutes / AfterHoursService.ROUND_MINUTES) +// * AfterHoursService.ROUND_MINUTES; +// this.logger.debug(`roundToNearestQuarterMinute -> raw=${minutes.toFixed(1)}min, rounded= ${rounded}min`); +// return rounded; +// } + +// public computeAfterHours(start: Date, end:Date): number { +// if(end.getTime() <= start.getTime()) { +// throw new BadRequestException('The end cannot be before the starting of the shift'); +// } + +// if (start.toDateString() !== end.toDateString()) { +// throw new BadRequestException('you cannot enter a shift that start in a day and end in the next' + +// 'You must create 2 instances, one on the first day and the second during the next day.'); +// } + +// const pre_min = this.getPreBusinessMinutes(start, end); +// const post_min = this.getPostBusinessMinutes(start, end); +// const raw_aftermin = pre_min + post_min; + +// const rounded_min = this.roundToNearestQUarterMinute(raw_aftermin); + +// const hours = rounded_min / 60; +// const result = parseFloat(hours.toFixed(2)); + +// this.logger.debug(`computeAfterHours -> raw_aftermin = ${raw_aftermin.toFixed(1)}min, + +// rounded = ${rounded_min}min, hours = ${result.toFixed(2)}`); +// return result; +// } +// } + diff --git a/src/time-and-attendance/modules/expenses/~misc_deprecated-files/create-expense.dto.ts b/src/~misc_deprecated-files/create-expense.dto.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/~misc_deprecated-files/create-expense.dto.ts rename to src/~misc_deprecated-files/create-expense.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/create-timesheet.dto.ts b/src/~misc_deprecated-files/create-timesheet.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/create-timesheet.dto.ts rename to src/~misc_deprecated-files/create-timesheet.dto.ts diff --git a/src/~misc_deprecated-files/date-time.constant.ts b/src/~misc_deprecated-files/date-time.constant.ts new file mode 100644 index 0000000..293a3d5 --- /dev/null +++ b/src/~misc_deprecated-files/date-time.constant.ts @@ -0,0 +1,2 @@ +// export const MS_PER_DAY = 86_400_000; +// export const MS_PER_HOUR = 3_600_000; \ No newline at end of file diff --git a/src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses-command.service.ts b/src/~misc_deprecated-files/expenses-command.service.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses-command.service.ts rename to src/~misc_deprecated-files/expenses-command.service.ts diff --git a/src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses-query.service.ts b/src/~misc_deprecated-files/expenses-query.service.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses-query.service.ts rename to src/~misc_deprecated-files/expenses-query.service.ts diff --git a/src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses.controller.ts b/src/~misc_deprecated-files/expenses.controller.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses.controller.ts rename to src/~misc_deprecated-files/expenses.controller.ts diff --git a/src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses.types.interfaces.ts b/src/~misc_deprecated-files/expenses.types.interfaces.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses.types.interfaces.ts rename to src/~misc_deprecated-files/expenses.types.interfaces.ts diff --git a/src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses.utils.ts b/src/~misc_deprecated-files/expenses.utils.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/~misc_deprecated-files/expenses.utils.ts rename to src/~misc_deprecated-files/expenses.utils.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/get-shift-overview.dto.ts b/src/~misc_deprecated-files/get-shift-overview.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/get-shift-overview.dto.ts rename to src/~misc_deprecated-files/get-shift-overview.dto.ts diff --git a/src/~misc_deprecated-files/leave-requests.select.ts b/src/~misc_deprecated-files/leave-requests.select.ts new file mode 100644 index 0000000..64453b4 --- /dev/null +++ b/src/~misc_deprecated-files/leave-requests.select.ts @@ -0,0 +1,23 @@ +// import { Prisma } from "@prisma/client"; + +// //custom prisma select to avoid employee_id exposure +// export const leaveRequestsSelect = { +// id: true, +// bank_code_id: true, +// leave_type: true, +// date: true, +// payable_hours: true, +// requested_hours: true, +// comment: true, +// approval_status: true, +// employee: { select: { +// id: true, +// user: { select: { +// email: true, +// first_name: true, +// last_name: true, +// }}, +// }}, +// } satisfies Prisma.LeaveRequestsSelect; + +// export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>; diff --git a/src/~misc_deprecated-files/regex.constant.ts b/src/~misc_deprecated-files/regex.constant.ts new file mode 100644 index 0000000..4dd4576 --- /dev/null +++ b/src/~misc_deprecated-files/regex.constant.ts @@ -0,0 +1,2 @@ +// const HH_MM_REGEX = /^([01]\d|2[0-3]):[0-5]\d$/; +// const DATE_ISO_FORMAT = /^\d{4}-\d{2}-\d{2}$/; \ No newline at end of file diff --git a/src/time-and-attendance/modules/expenses/~misc_deprecated-files/search-expense.dto.ts b/src/~misc_deprecated-files/search-expense.dto.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/~misc_deprecated-files/search-expense.dto.ts rename to src/~misc_deprecated-files/search-expense.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/search-timesheet.dto.ts b/src/~misc_deprecated-files/search-timesheet.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/search-timesheet.dto.ts rename to src/~misc_deprecated-files/search-timesheet.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-command.service.ts b/src/~misc_deprecated-files/shifts-command.service.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-command.service.ts rename to src/~misc_deprecated-files/shifts-command.service.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-overview-row.interface.ts b/src/~misc_deprecated-files/shifts-overview-row.interface.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-overview-row.interface.ts rename to src/~misc_deprecated-files/shifts-overview-row.interface.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-query.service.ts b/src/~misc_deprecated-files/shifts-query.service.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-query.service.ts rename to src/~misc_deprecated-files/shifts-query.service.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-upsert.types.ts b/src/~misc_deprecated-files/shifts-upsert.types.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts-upsert.types.ts rename to src/~misc_deprecated-files/shifts-upsert.types.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.controller.ts b/src/~misc_deprecated-files/shifts.controller.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.controller.ts rename to src/~misc_deprecated-files/shifts.controller.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.helpers.ts b/src/~misc_deprecated-files/shifts.helpers.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.helpers.ts rename to src/~misc_deprecated-files/shifts.helpers.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.utils.ts b/src/~misc_deprecated-files/shifts.utils.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/shifts.utils.ts rename to src/~misc_deprecated-files/shifts.utils.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet-period.dto.ts b/src/~misc_deprecated-files/timesheet-period.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet-period.dto.ts rename to src/~misc_deprecated-files/timesheet-period.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.helpers.ts b/src/~misc_deprecated-files/timesheet.helpers.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.helpers.ts rename to src/~misc_deprecated-files/timesheet.helpers.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.mappers.ts b/src/~misc_deprecated-files/timesheet.mappers.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.mappers.ts rename to src/~misc_deprecated-files/timesheet.mappers.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.selectors.ts b/src/~misc_deprecated-files/timesheet.selectors.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.selectors.ts rename to src/~misc_deprecated-files/timesheet.selectors.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.types.ts b/src/~misc_deprecated-files/timesheet.types.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.types.ts rename to src/~misc_deprecated-files/timesheet.types.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.utils.ts b/src/~misc_deprecated-files/timesheet.utils.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheet.utils.ts rename to src/~misc_deprecated-files/timesheet.utils.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-command.service.ts b/src/~misc_deprecated-files/timesheets-command.service.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-command.service.ts rename to src/~misc_deprecated-files/timesheets-command.service.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-query.service.ts b/src/~misc_deprecated-files/timesheets-query.service.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets-query.service.ts rename to src/~misc_deprecated-files/timesheets-query.service.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets.controller.ts b/src/~misc_deprecated-files/timesheets.controller.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/~misc_deprecated-files/timesheets.controller.ts rename to src/~misc_deprecated-files/timesheets.controller.ts diff --git a/src/time-and-attendance/modules/expenses/~misc_deprecated-files/update-expense.dto.ts b/src/~misc_deprecated-files/update-expense.dto.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/~misc_deprecated-files/update-expense.dto.ts rename to src/~misc_deprecated-files/update-expense.dto.ts diff --git a/src/time-and-attendance/modules/expenses/~misc_deprecated-files/upsert-expense.dto.ts b/src/~misc_deprecated-files/upsert-expense.dto.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/~misc_deprecated-files/upsert-expense.dto.ts rename to src/~misc_deprecated-files/upsert-expense.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/upsert-shift.dto.ts b/src/~misc_deprecated-files/upsert-shift.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/~misc_deprecated-files/upsert-shift.dto.ts rename to src/~misc_deprecated-files/upsert-shift.dto.ts diff --git a/src/~misc_deprecated-files/utils.constant.ts b/src/~misc_deprecated-files/utils.constant.ts new file mode 100644 index 0000000..005ac6c --- /dev/null +++ b/src/~misc_deprecated-files/utils.constant.ts @@ -0,0 +1 @@ +// export const COMMENT_MAX_LENGTH = 280; \ No newline at end of file From e7a50df5e511840f15e8e6484e422f4ab579ef27 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 27 Oct 2025 12:08:06 -0400 Subject: [PATCH 10/57] refactor(selects): moved select: lines to shared file --- .../shifts/services/shifts-get.service.ts | 13 ++------- .../shifts/services/shifts-upsert.service.ts | 29 +++---------------- .../time-and-attendance.module.ts | 2 -- .../utils/selects.utils.ts | 11 +++++++ 4 files changed, 17 insertions(+), 38 deletions(-) diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts index 5d17686..af1e947 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts @@ -2,6 +2,7 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { GetShiftDto } from "../dtos/shift-get.dto"; import { toStringFromDate, toStringFromHHmm } from "../../../../utils/date-time-helpers"; +import { shift_select } from "src/time-and-attendance/utils/selects.utils"; /** * _____________________________________________________________________________________ @@ -26,17 +27,7 @@ export class ShiftsGetService { const rows = await this.prisma.shifts.findMany({ where: { id: { in: shift_ids } }, - select: { - id: true, - timesheet_id: true, - bank_code_id: true, - date: true, - start_time: true, - end_time: true, - is_remote: true, - is_approved: true, - comment: true, - } + select: shift_select, }); if(rows.length !== shift_ids.length) { diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts index 6049359..34d605c 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts @@ -5,6 +5,7 @@ import { updateShiftDto } from "../dtos/shift-update.dto"; import { PrismaService } from "src/prisma/prisma.service"; import { GetShiftDto } from "../dtos/shift-get.dto"; import { ShiftDto } from "../dtos/shift-create.dto"; +import { shift_select } from "src/time-and-attendance/utils/selects.utils"; type Normalized = { date: Date; start_time: Date; end_time: Date; }; @@ -136,10 +137,7 @@ export class ShiftsUpsertService { is_remote: dto.is_remote, comment: dto.comment ?? undefined, }, - select: { - timesheet_id: true, bank_code_id: true, date: true, - start_time: true, end_time: true, is_remote: true, is_approved: true, comment: true, - }, + select: shift_select, }); existing.push({ start_time: row.start_time, end_time: row.end_time }); @@ -181,17 +179,7 @@ export class ShiftsUpsertService { const shift_ids = updates.map(update_shift => update_shift.id); const rows = await tx.shifts.findMany({ where: { id: { in: shift_ids } }, - select: { - id: true, - timesheet_id: true, - bank_code_id: true, - date: true, - start_time: true, - end_time: true, - is_remote: true, - is_approved: true, - comment: true, - }, + select: shift_select, }); const regroup_id = new Map(rows.map(r => [r.id, r])); @@ -295,16 +283,7 @@ export class ShiftsUpsertService { const row = await tx.shifts.update({ where: { id: planned.exist_shift.id }, data, - select: { - timesheet_id: true, - bank_code_id: true, - date: true, - start_time: true, - end_time: true, - is_remote: true, - is_approved: true, - comment: true, - }, + select: shift_select, }); const summary_new = await this.overtime.getWeekOvertimeSummary(row.timesheet_id, planned.exist_shift.date, tx); diff --git a/src/time-and-attendance/time-and-attendance.module.ts b/src/time-and-attendance/time-and-attendance.module.ts index e3eff36..cee8cc5 100644 --- a/src/time-and-attendance/time-and-attendance.module.ts +++ b/src/time-and-attendance/time-and-attendance.module.ts @@ -3,7 +3,6 @@ import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/ti import { SchedulePresetsQueryService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service"; import { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller"; import { ExpensesArchivalService } from "src/time-and-attendance/modules/expenses/services/expenses-archival.service"; -// import { LeaveRequestController } from "src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller"; import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; @@ -23,7 +22,6 @@ import { Module } from "@nestjs/common"; ShiftController, SchedulePresetsController, ExpenseController, - // LeaveRequestController, ], providers: [ diff --git a/src/time-and-attendance/utils/selects.utils.ts b/src/time-and-attendance/utils/selects.utils.ts index ac5845f..0140936 100644 --- a/src/time-and-attendance/utils/selects.utils.ts +++ b/src/time-and-attendance/utils/selects.utils.ts @@ -14,6 +14,17 @@ export const expense_select = { is_approved: true, } satisfies Prisma.ExpensesSelect; +export const shift_select = { + id: true, + timesheet_id: true, + bank_code_id: true, + date: true, + start_time: true, + end_time: true, + is_remote: true, + is_approved: true, + comment: true, +} satisfies Prisma.ShiftsSelect; export const leaveRequestsSelect = { id: true, From 28f811e1fb1e270ff4690ada7dc472eda41119fc Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 27 Oct 2025 13:28:39 -0400 Subject: [PATCH 11/57] refactor(modules): clean up module.ts files to match new imports/controllers/providers --- .../domains/business-logics.module.ts | 2 -- .../modules/expenses/expenses.module.ts | 3 +-- .../leave-requests/leave-requests.module.ts | 21 +++++++------------ .../services/sick-leave-requests.service.ts | 1 - .../modules/pay-period/pay-periods.module.ts | 6 +----- .../schedule-presets.module.ts | 2 -- .../timesheets/timesheets.module.ts | 3 +-- .../time-and-attendance.module.ts | 16 +++++++------- .../utils/regex.constants.ts | 2 ++ src/~misc_deprecated-files/regex.constant.ts | 3 +-- 10 files changed, 21 insertions(+), 38 deletions(-) create mode 100644 src/time-and-attendance/utils/regex.constants.ts diff --git a/src/time-and-attendance/domains/business-logics.module.ts b/src/time-and-attendance/domains/business-logics.module.ts index 5b103b8..91774ec 100644 --- a/src/time-and-attendance/domains/business-logics.module.ts +++ b/src/time-and-attendance/domains/business-logics.module.ts @@ -3,13 +3,11 @@ import { OvertimeService } from "./services/overtime.service"; import { VacationService } from "./services/vacation.service"; import { HolidayService } from "./services/holiday.service"; import { MileageService } from "./services/mileage.service"; -import { PrismaService } from "src/prisma/prisma.service"; import { Module } from "@nestjs/common"; @Module({ providers: [ - PrismaService, HolidayService, MileageService, OvertimeService, diff --git a/src/time-and-attendance/modules/expenses/expenses.module.ts b/src/time-and-attendance/modules/expenses/expenses.module.ts index 17da55a..6ffff02 100644 --- a/src/time-and-attendance/modules/expenses/expenses.module.ts +++ b/src/time-and-attendance/modules/expenses/expenses.module.ts @@ -1,11 +1,10 @@ -import { ExpensesArchivalService } from "./services/expenses-archival.service"; import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; import { ExpenseController } from "src/time-and-attendance/modules/expenses/controllers/expense.controller"; import { Module } from "@nestjs/common"; @Module({ controllers: [ ExpenseController ], - providers: [ ExpenseUpsertService, ExpensesArchivalService ], + providers: [ ExpenseUpsertService ], }) export class ExpensesModule {} \ No newline at end of file diff --git a/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts b/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts index 25fc768..21b9e23 100644 --- a/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts +++ b/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts @@ -1,26 +1,19 @@ import { LeaveRequestController } from "src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller"; import { LeaveRequestsService } from "src/time-and-attendance/modules/leave-requests/services/leave-request.service"; import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; -import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; -import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; -import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; -import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; -import { PrismaService } from "src/prisma/prisma.service"; import { ShiftsModule } from "src/time-and-attendance/modules/time-tracker/shifts/shifts.module"; +import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; import { Module } from "@nestjs/common"; @Module({ - imports: [BusinessLogicsModule, ShiftsModule ], - controllers: [LeaveRequestController], - providers: [ - VacationService, - SickLeaveService, - HolidayService, - LeaveRequestsService, - PrismaService, - LeaveRequestsUtils, + imports: [ + BusinessLogicsModule, + ShiftsModule, + SharedModule ], + controllers: [LeaveRequestController], + providers: [LeaveRequestsService], }) export class LeaveRequestsModule {} \ No newline at end of file diff --git a/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts b/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts index 13783b2..969d8a5 100644 --- a/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts +++ b/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts @@ -6,7 +6,6 @@ import { SickLeaveService } from "src/time-and-attendance/domains/services/sick- import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto"; import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto"; import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper"; -import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; diff --git a/src/time-and-attendance/modules/pay-period/pay-periods.module.ts b/src/time-and-attendance/modules/pay-period/pay-periods.module.ts index 2a26c5a..3670c72 100644 --- a/src/time-and-attendance/modules/pay-period/pay-periods.module.ts +++ b/src/time-and-attendance/modules/pay-period/pay-periods.module.ts @@ -1,14 +1,10 @@ import { PayPeriodsQueryService } from "./services/pay-periods-query.service"; import { PayPeriodsController } from "./controllers/pay-periods.controller"; -import { PrismaService } from "src/prisma/prisma.service"; import { Module } from "@nestjs/common"; @Module({ - providers: [ - PayPeriodsQueryService, - PrismaService, - ], controllers: [PayPeriodsController], + providers: [PayPeriodsQueryService], }) export class PayperiodsModule {} \ No newline at end of file diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts index 8194655..70d34f5 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts @@ -2,7 +2,6 @@ import { Module } from "@nestjs/common"; import { SchedulePresetsCommandService } from "./services/schedule-presets-command.service"; import { SchedulePresetsQueryService } from "./services/schedule-presets-query.service"; import { SchedulePresetsController } from "./controller/schedule-presets.controller"; -import { PrismaService } from "src/prisma/prisma.service"; import { SchedulePresetsApplyService } from "./services/schedule-presets-apply.service"; import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; @@ -10,7 +9,6 @@ import { SharedModule } from "src/time-and-attendance/modules/shared/shared.modu imports: [SharedModule], controllers: [SchedulePresetsController], providers: [ - PrismaService, SchedulePresetsCommandService, SchedulePresetsQueryService, SchedulePresetsApplyService, diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts b/src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts index 930c70f..d9375cf 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts @@ -5,12 +5,11 @@ import { SharedModule } from '../../shared/shared.module'; import { Module } from '@nestjs/common'; @Module({ - imports: [], + imports: [SharedModule], controllers: [TimesheetController], providers: [ TimesheetArchiveService, GetTimesheetsOverviewService, - SharedModule, ], exports: [], }) diff --git a/src/time-and-attendance/time-and-attendance.module.ts b/src/time-and-attendance/time-and-attendance.module.ts index cee8cc5..31155e9 100644 --- a/src/time-and-attendance/time-and-attendance.module.ts +++ b/src/time-and-attendance/time-and-attendance.module.ts @@ -1,39 +1,39 @@ import { SchedulePresetsCommandService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service"; import { SchedulePresetsQueryService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service"; +import { SchedulePresetsApplyService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service"; import { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller"; -import { ExpensesArchivalService } from "src/time-and-attendance/modules/expenses/services/expenses-archival.service"; import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; import { TimesheetController } from "src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller"; import { ExpenseController } from "src/time-and-attendance/modules/expenses/controllers/expense.controller"; -import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; import { PayperiodsModule } from "src/time-and-attendance/modules/pay-period/pay-periods.module"; import { ShiftsGetService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service"; import { ShiftController } from "src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller"; +import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; import { Module } from "@nestjs/common"; @Module({ - imports: [BusinessLogicsModule, PayperiodsModule], + imports: [ + BusinessLogicsModule, + PayperiodsModule, + SharedModule, + ], controllers: [ TimesheetController, ShiftController, SchedulePresetsController, ExpenseController, - ], providers: [ GetTimesheetsOverviewService, ShiftsGetService, ShiftsUpsertService, ExpenseUpsertService, - ExpensesArchivalService, - EmailToIdResolver, SchedulePresetsCommandService, - BankCodesResolver, SchedulePresetsQueryService, + SchedulePresetsApplyService, ], exports: [], }) export class TimeAndAttendanceModule{}; \ No newline at end of file diff --git a/src/time-and-attendance/utils/regex.constants.ts b/src/time-and-attendance/utils/regex.constants.ts new file mode 100644 index 0000000..d192d70 --- /dev/null +++ b/src/time-and-attendance/utils/regex.constants.ts @@ -0,0 +1,2 @@ +const DATE_ISO_FORMAT = /^\d{4}-\d{2}-\d{2}$/; +const HH_MM_REGEX = /^([01]\d|2[0-3]):[0-5]\d$/; \ No newline at end of file diff --git a/src/~misc_deprecated-files/regex.constant.ts b/src/~misc_deprecated-files/regex.constant.ts index 4dd4576..8b13789 100644 --- a/src/~misc_deprecated-files/regex.constant.ts +++ b/src/~misc_deprecated-files/regex.constant.ts @@ -1,2 +1 @@ -// const HH_MM_REGEX = /^([01]\d|2[0-3]):[0-5]\d$/; -// const DATE_ISO_FORMAT = /^\d{4}-\d{2}-\d{2}$/; \ No newline at end of file + From 488f0341cc4ce666ce72eb237bb3925fa69429a5 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 27 Oct 2025 13:41:20 -0400 Subject: [PATCH 12/57] feat(timesheets): added timesheet_id to shifts return objects --- .../modules/time-tracker/timesheets/dtos/timesheet.dto.ts | 1 + .../timesheets/services/timesheet-get-overview.service.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/dtos/timesheet.dto.ts b/src/time-and-attendance/modules/time-tracker/timesheets/dtos/timesheet.dto.ts index 3d30c8b..7e72aac 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/dtos/timesheet.dto.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/dtos/timesheet.dto.ts @@ -36,6 +36,7 @@ export class TotalExpenses { } export class Shift { + timesheet_id: number; date: string; start_time: string; end_time: string; diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts index a3c3c91..6576477 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -122,6 +122,7 @@ export class GetTimesheetsOverviewService { const expenses_source = expenses_by_date.get(date_iso) ?? []; //inner map of shifts const shifts = shifts_source.map((shift) => ({ + timesheet_id: shift.timesheet_id, date: toStringFromDate(shift.date), start_time: toHHmmFromDate(shift.start_time), end_time: toHHmmFromDate(shift.end_time), From 4cb01de970a9c6bdb5609a6d4b8f721d048fc24e Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 27 Oct 2025 15:23:28 -0400 Subject: [PATCH 13/57] feat(time_and_attendance): clean files of deprecated imports and moved utils to shared utils folder. Modified @Body for create and update shifts to use only an array of shifts --- docs/swagger/swagger-spec.json | 28 ++++---- .../services/expense-upsert.service.ts | 2 +- .../mappers/leave-requests.mapper.ts | 4 +- .../utils/leave-request.transform.ts | 4 +- .../utils/leave-request.util.ts | 2 +- .../modules/pay-period/utils/pay-year.util.ts | 12 ++-- .../shared/utils/resolve-timesheet.utils.ts | 2 +- .../controller/schedule-presets.controller.ts | 4 +- .../dtos/create-schedule-presets.dto.ts | 2 +- .../schedule-presets-apply.service.ts | 4 +- .../schedule-presets-query.service.ts | 2 +- .../types/schedule-presets.types.ts | 21 ------ .../shifts/controllers/shift.controller.ts | 32 +++++----- .../shifts/dtos/shift-create.dto.ts | 1 + .../shifts/dtos/shift-update.dto.ts | 12 ++-- .../services/shifts-archival.service.ts | 1 - .../shifts/services/shifts-get.service.ts | 2 +- .../shifts/services/shifts-upsert.service.ts | 55 ++++++++-------- .../helpers/timesheets-date-time-helpers.ts | 36 ----------- .../timesheet-get-overview.service.ts | 22 +------ .../utils/constants.utils.ts | 11 ++++ ...ate-time-helpers.ts => date-time.utils.ts} | 24 ++++++- .../mappers.utils.ts} | 0 .../utils/regex.constants.ts | 2 - .../utils/selects.utils.ts | 3 - src/time-and-attendance/utils/type.utils.ts | 64 +++++++++++++++++++ 26 files changed, 186 insertions(+), 166 deletions(-) delete mode 100644 src/time-and-attendance/modules/time-tracker/schedule-presets/types/schedule-presets.types.ts delete mode 100644 src/time-and-attendance/modules/time-tracker/timesheets/helpers/timesheets-date-time-helpers.ts create mode 100644 src/time-and-attendance/utils/constants.utils.ts rename src/time-and-attendance/utils/{date-time-helpers.ts => date-time.utils.ts} (65%) rename src/time-and-attendance/{modules/time-tracker/schedule-presets/mappers/schedule-presets.mappers.ts => utils/mappers.utils.ts} (100%) delete mode 100644 src/time-and-attendance/utils/regex.constants.ts create mode 100644 src/time-and-attendance/utils/type.utils.ts diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 1ca3c19..4167a3f 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -377,19 +377,10 @@ ] } }, - "/shift/{timesheet_id}": { + "/shift/create": { "post": { "operationId": "ShiftController_createBatch", - "parameters": [ - { - "name": "timesheet_id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], + "parameters": [], "requestBody": { "required": true, "content": { @@ -413,10 +404,23 @@ ] } }, - "/shift": { + "/shift/update": { "patch": { "operationId": "ShiftController_updateBatch", "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, "responses": { "200": { "description": "" diff --git a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts index bc35988..b2e8766 100644 --- a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts @@ -1,5 +1,5 @@ -import { toDateFromString, toStringFromDate } from "src/time-and-attendance/utils/date-time-helpers"; +import { toDateFromString, toStringFromDate } from "src/time-and-attendance/utils/date-time.utils"; import { Injectable, NotFoundException } from "@nestjs/common"; import { updateExpenseDto } from "../dtos/expense-update.dto"; import { GetExpenseDto } from "../dtos/expense-get.dto"; diff --git a/src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper.ts b/src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper.ts index 48d1214..9f823fc 100644 --- a/src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper.ts +++ b/src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper.ts @@ -1,6 +1,6 @@ -import { Prisma } from "@prisma/client"; import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; -import { LeaveRequestRow } from "src/time-and-attendance/utils/selects.utils"; +import { LeaveRequestRow } from "src/time-and-attendance/utils/type.utils"; +import { Prisma } from "@prisma/client"; const toNum = (value?: Prisma.Decimal | null) => value !== null && value !== undefined ? Number(value) : undefined; diff --git a/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts b/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts index f5501c0..042c796 100644 --- a/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts +++ b/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts @@ -1,8 +1,8 @@ -import { LeaveRequestRow } from 'src/time-and-attendance/utils/selects.utils'; +import { LeaveRequestArchiveRow } from './leave-requests-archive.select'; import { LeaveRequestViewDto } from '../dtos/leave-request-view.dto'; import { mapArchiveRowToView } from '../mappers/leave-requests-archive.mapper'; +import { LeaveRequestRow } from 'src/time-and-attendance/utils/type.utils'; import { mapRowToView } from '../mappers/leave-requests.mapper'; -import { LeaveRequestArchiveRow } from './leave-requests-archive.select'; /** Active (table leave_requests) : proxy to base mapper */ export function mapRowToViewWithDays(row: LeaveRequestRow): LeaveRequestViewDto { diff --git a/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts b/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts index 776c56e..7c66afb 100644 --- a/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts +++ b/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts @@ -4,7 +4,7 @@ import { PrismaService } from "src/prisma/prisma.service"; import { LeaveTypes } from "@prisma/client"; // import { toDateOnly, toStringFromDate } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; import { UpsertAction } from "src/time-and-attendance/modules/shared/types/upsert-actions.types"; -import { toDateFromString, toStringFromDate } from "src/time-and-attendance/utils/date-time-helpers"; +import { toDateFromString, toStringFromDate } from "src/time-and-attendance/utils/date-time.utils"; // import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; @Injectable() diff --git a/src/time-and-attendance/modules/pay-period/utils/pay-year.util.ts b/src/time-and-attendance/modules/pay-period/utils/pay-year.util.ts index 1dbf0d0..df45170 100644 --- a/src/time-and-attendance/modules/pay-period/utils/pay-year.util.ts +++ b/src/time-and-attendance/modules/pay-period/utils/pay-year.util.ts @@ -1,20 +1,18 @@ -import { toStringFromDate, toUTCDate } from "src/time-and-attendance/utils/date-time-helpers"; +import { toStringFromDate, toUTCDateFromString } from "src/time-and-attendance/utils/date-time.utils"; export const ANCHOR_ISO = '2023-12-17'; // ancre date -const PERIOD_DAYS = 14; -const PERIODS_PER_YEAR = 26; -const MS_PER_DAY = 86_400_000; + export function payYearOfDate(date: string | Date, anchorISO = ANCHOR_ISO): number { - const ANCHOR = toUTCDate(anchorISO); - const d = toUTCDate(date); + const ANCHOR = toUTCDateFromString(anchorISO); + const d = toUTCDateFromString(date); const days = Math.floor((+d - +ANCHOR) / MS_PER_DAY); const cycles = Math.floor(days / (PERIODS_PER_YEAR * PERIOD_DAYS)); return ANCHOR.getUTCFullYear() + 1 + cycles; } //compute labels for periods export function computePeriod(pay_year: number, period_no: number, anchorISO = ANCHOR_ISO) { - const ANCHOR = toUTCDate(anchorISO); + const ANCHOR = toUTCDateFromString(anchorISO); const cycles = pay_year - (ANCHOR.getUTCFullYear() + 1); const offsetPeriods = cycles * PERIODS_PER_YEAR + (period_no - 1); const start = new Date(+ANCHOR + offsetPeriods * PERIOD_DAYS * MS_PER_DAY); diff --git a/src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts b/src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts index a992af8..f556a8e 100644 --- a/src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts +++ b/src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts @@ -1,6 +1,6 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { Prisma, PrismaClient } from "@prisma/client"; -import { weekStartSunday } from "src/time-and-attendance/utils/date-time-helpers"; +import { weekStartSunday } from "src/time-and-attendance/utils/date-time.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { EmailToIdResolver } from "./resolve-email-id.utils"; diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index 79a8976..f02b958 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -1,8 +1,8 @@ import { BadRequestException, Body, Controller, Get, NotFoundException, Param, Post, Put, Query } from "@nestjs/common"; -import { SchedulePresetsDto } from "../dtos/create-schedule-presets.dto"; import { SchedulePresetsCommandService } from "../services/schedule-presets-command.service"; -import { UpsertAction } from "src/time-and-attendance/modules/shared/types/upsert-actions.types"; import { SchedulePresetsQueryService } from "../services/schedule-presets-query.service"; +import { SchedulePresetsDto } from "../dtos/create-schedule-presets.dto"; +import { UpsertAction } from "src/time-and-attendance/modules/shared/types/upsert-actions.types"; @Controller('schedule-presets') export class SchedulePresetsController { diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts index 7bd822f..cb1c512 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts @@ -1,4 +1,4 @@ -import { ArrayMinSize, IsArray, IsBoolean, IsEmail, IsOptional, IsString } from "class-validator"; +import { ArrayMinSize, IsArray, IsBoolean, IsOptional, IsString } from "class-validator"; import { SchedulePresetShiftsDto } from "./create-schedule-preset-shifts.dto"; export class SchedulePresetsDto { diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts index 98bde74..7bad7f6 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts @@ -1,9 +1,9 @@ import { BadRequestException, ConflictException, Injectable, NotFoundException } from "@nestjs/common"; import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; import { PrismaService } from "src/prisma/prisma.service"; -import { ApplyResult } from "../types/schedule-presets.types"; import { Prisma, Weekday } from "@prisma/client"; -import { WEEKDAY } from "../mappers/schedule-presets.mappers"; +import { WEEKDAY } from "../../../../utils/mappers.utils"; +import { ApplyResult } from "src/time-and-attendance/utils/type.utils"; @Injectable() export class SchedulePresetsApplyService { diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts index e406151..74d464b 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts @@ -1,8 +1,8 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; import { PrismaService } from "src/prisma/prisma.service"; -import { PresetResponse, ShiftResponse } from "../types/schedule-presets.types"; import { Prisma } from "@prisma/client"; +import { PresetResponse, ShiftResponse } from "src/time-and-attendance/utils/type.utils"; @Injectable() export class SchedulePresetsQueryService { diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/types/schedule-presets.types.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/types/schedule-presets.types.ts deleted file mode 100644 index ea2a3cd..0000000 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/types/schedule-presets.types.ts +++ /dev/null @@ -1,21 +0,0 @@ -export type ShiftResponse = { - week_day: string; - sort_order: number; - start_time: string; - end_time: string; - is_remote: boolean; - type: string; -}; - -export type PresetResponse = { - id: number; - name: string; - is_default: boolean; - shifts: ShiftResponse[]; -} - -export type ApplyResult = { - timesheet_id: number; - created: number; - skipped: number; -} \ No newline at end of file diff --git a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts index 6f0358f..1658e3a 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts @@ -1,32 +1,30 @@ -import { BadRequestException, Body, Controller, Delete, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; -import { CreateResult, ShiftsUpsertService, UpdateResult } from "../services/shifts-upsert.service"; -import { updateShiftDto } from "../dtos/shift-update.dto"; +import { BadRequestException, Body, Controller, Delete, Param, Patch, Post } from "@nestjs/common"; +import { CreateResult, UpdateResult } from "src/time-and-attendance/utils/type.utils"; +import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; import { ShiftDto } from "../dtos/shift-create.dto"; +import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; @Controller('shift') export class ShiftController { constructor( private readonly upsert_service: ShiftsUpsertService ){} - @Post(':timesheet_id') + @Post('create') createBatch( - @Param('timesheet_id', ParseIntPipe) timesheet_id: number, @Body()dtos: ShiftDto[]): Promise { const list = Array.isArray(dtos) ? dtos : []; - if(list.length === 0) throw new BadRequestException('Body is missing or invalid (create shifts)') - return this.upsert_service.createShifts(timesheet_id, dtos) + if(list.length === 0) throw new BadRequestException('Body is missing or invalid (create shifts)'); + return this.upsert_service.createShifts(dtos) } - @Patch() + + //change Body to receive dtos + @Patch('update') updateBatch( - @Body() body: { updates: { id: number; dto: updateShiftDto }[] }): Promise{ - const updates = Array.isArray(body?.updates) - ? body.updates.filter(update => Number.isFinite(update?.id) && typeof update.dto === "object") - : []; - if(updates.length === 0) { - throw new BadRequestException(`Body is missing or invalid (update shifts)`); - } - return this.upsert_service.updateShifts(updates); + @Body() dtos: UpdateShiftDto[]): Promise{ + const list = Array.isArray(dtos) ? dtos: []; + if(list.length === 0) throw new BadRequestException('Body is missing or invalid (update shifts)'); + return this.upsert_service.updateShifts(dtos); } @Delete(':shift_id') @@ -34,4 +32,4 @@ export class ShiftController { return this.upsert_service.deleteShift(shift_id); } -} \ No newline at end of file +} diff --git a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto.ts b/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto.ts index 65413da..9393b04 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto.ts @@ -1,6 +1,7 @@ import { IsBoolean, IsInt, IsOptional, IsString, MaxLength } from "class-validator"; export class ShiftDto { + @IsInt() @IsOptional() id: number; @IsInt() timesheet_id!: number; @IsInt() bank_code_id!: number; diff --git a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts b/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts index 95ef660..a9f2901 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts @@ -1,7 +1,11 @@ import { PartialType, OmitType } from "@nestjs/swagger"; +import { IsInt } from "class-validator"; import { ShiftDto } from "./shift-create.dto"; -export class updateShiftDto extends PartialType ( - //allows update using ShiftDto and preventing OmitType variables to be modified - OmitType(ShiftDto, [ 'is_approved', 'timesheet_id'] as const) -){} \ No newline at end of file +export class UpdateShiftDto extends PartialType( + // allows update using ShiftDto and preventing OmitType variables to be modified + OmitType(ShiftDto, ['is_approved', 'timesheet_id'] as const), +) { + @IsInt() + id!: number; +} diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts index 9a6671a..6f60390 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts @@ -2,7 +2,6 @@ import { Injectable } from "@nestjs/common"; import { ShiftsArchive } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; - /** * _____________________________________________________________________________________ * diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts index af1e947..67f887a 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts @@ -1,7 +1,7 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { GetShiftDto } from "../dtos/shift-get.dto"; -import { toStringFromDate, toStringFromHHmm } from "../../../../utils/date-time-helpers"; +import { toStringFromDate, toStringFromHHmm } from "../../../../utils/date-time.utils"; import { shift_select } from "src/time-and-attendance/utils/selects.utils"; /** diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts index 34d605c..e9c18d8 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts @@ -1,26 +1,12 @@ -import { toDateFromString, toHHmmFromString, toStringFromDate, toStringFromHHmm } from "../../../../utils/date-time-helpers"; +import { CreateResult, NormedOk, NormedErr, UpdatePayload, UpdateResult, Normalized, UpdateChanges } from "src/time-and-attendance/utils/type.utils"; +import { toDateFromString, toHHmmFromString, toStringFromDate, toStringFromHHmm } from "../../../../utils/date-time.utils"; import { BadRequestException, ConflictException, Injectable, NotFoundException } from "@nestjs/common"; -import { OvertimeService, WeekOvertimeSummary } from "src/time-and-attendance/domains/services/overtime.service"; -import { updateShiftDto } from "../dtos/shift-update.dto"; +import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; import { PrismaService } from "src/prisma/prisma.service"; +import { shift_select } from "src/time-and-attendance/utils/selects.utils"; import { GetShiftDto } from "../dtos/shift-get.dto"; import { ShiftDto } from "../dtos/shift-create.dto"; -import { shift_select } from "src/time-and-attendance/utils/selects.utils"; - -type Normalized = { date: Date; start_time: Date; end_time: Date; }; - -export type ShiftWithOvertimeDto = { - shift: GetShiftDto; - overtime: WeekOvertimeSummary; -}; - -export type CreateResult = { ok: true; data: ShiftWithOvertimeDto } | { ok: false; error: any }; -export type UpdatePayload = { id: number; dto: updateShiftDto }; -export type UpdateResult = { ok: true; id: number; data: ShiftWithOvertimeDto } | { ok: false; id: number; error: any }; -export type DeleteResult = { ok: true; id: number; overtime: WeekOvertimeSummary } | { ok: false; id: number; error: any }; - -type NormedOk = { index: number; dto: ShiftDto; normed: Normalized }; -type NormedErr = { index: number; error: any }; +import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; const overlaps = (a: { start: Date; end: Date }, b: { start: Date; end: Date }) => !(a.end <= b.start || a.start >= b.end); @@ -40,7 +26,7 @@ export class ShiftsUpsertService { //checks for overlaping shifts //create new shifts //calculate overtime - async createShifts(timesheet_id: number, dtos: ShiftDto[]): Promise { + async createShifts(dtos: ShiftDto[]): Promise { if (!Array.isArray(dtos) || dtos.length === 0) return []; const normed_shift: Array = dtos.map((dto, index) => { @@ -99,7 +85,7 @@ export class ShiftsUpsertService { const existing_date = new Map(); for (const d of unique_dates) { const rows = await tx.shifts.findMany({ - where: { timesheet_id, date: d }, + where: { date: d }, select: { start_time: true, end_time: true }, }); existing_date.set(d.getTime(), rows.map(r => ({ start_time: r.start_time, end_time: r.end_time }))); @@ -129,7 +115,7 @@ export class ShiftsUpsertService { const row = await tx.shifts.create({ data: { - timesheet_id, + timesheet_id: dto.timesheet_id, bank_code_id: dto.bank_code_id, date: normed.date, start_time: normed.start_time, @@ -142,7 +128,7 @@ export class ShiftsUpsertService { existing.push({ start_time: row.start_time, end_time: row.end_time }); - const summary = await this.overtime.getWeekOvertimeSummary(timesheet_id, normed.date, tx); + const summary = await this.overtime.getWeekOvertimeSummary(dto.timesheet_id, normed.date, tx); const shift: GetShiftDto = { timesheet_id: row.timesheet_id, bank_code_id: row.bank_code_id, @@ -172,8 +158,25 @@ export class ShiftsUpsertService { // update shifts in DB // recalculate overtime after update // return an updated version to display - async updateShifts(updates: UpdatePayload[]): Promise { - if (!Array.isArray(updates) || updates.length === 0) return []; + async updateShifts(dtos: UpdateShiftDto[]): Promise { + if (!Array.isArray(dtos) || dtos.length === 0) return []; + + const updates: UpdatePayload[] = dtos.map((item) => { + const { id, ...rest } = item; + if (!Number.isInteger(id)) { + throw new BadRequestException('Update shift payload is missing a valid id'); + } + + const changes: UpdateChanges = {}; + if (rest.date !== undefined) changes.date = rest.date; + if (rest.start_time !== undefined) changes.start_time = rest.start_time; + if (rest.end_time !== undefined) changes.end_time = rest.end_time; + if (rest.bank_code_id !== undefined) changes.bank_code_id = rest.bank_code_id; + if (rest.is_remote !== undefined) changes.is_remote = rest.is_remote; + if (rest.comment !== undefined) changes.comment = rest.comment; + + return { id, dto: changes }; + }); return this.prisma.$transaction(async (tx) => { const shift_ids = updates.map(update_shift => update_shift.id); @@ -343,4 +346,4 @@ export class ShiftsUpsertService { const end_time = toHHmmFromString(dto.end_time); return { date, start_time, end_time }; } -} \ No newline at end of file +} diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/helpers/timesheets-date-time-helpers.ts b/src/time-and-attendance/modules/time-tracker/timesheets/helpers/timesheets-date-time-helpers.ts deleted file mode 100644 index 3b06344..0000000 --- a/src/time-and-attendance/modules/time-tracker/timesheets/helpers/timesheets-date-time-helpers.ts +++ /dev/null @@ -1,36 +0,0 @@ -export function weekStartSunday(date_local: Date): Date { - const start = new Date(Date.UTC(date_local.getFullYear(), date_local.getMonth(), date_local.getDate())); - const dow = start.getDay(); - start.setDate(start.getDate() - dow); - start.setHours(0, 0, 0, 0); - return start; -} - -export const toDateFromString = ( date: Date | string):Date => { - const d = new Date(date); - return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())); -} - -export const sevenDaysFrom = (date: Date | string): Date[] => { - return Array.from({length: 7 }, (_,i) => { - const d = new Date(date); - d.setUTCDate(d.getUTCDate() + i ); - return d; - }); -} - -export const toStringFromDate = (date: Date | string): string => { - const d = toDateFromString(date); - const year = d.getUTCFullYear(); - const month = String(d.getUTCMonth() + 1).padStart(2, '0'); - const day = String(d.getUTCDate()).padStart(2, '0'); - return `${year}-${month}-${day}`; -} - -export const toHHmmFromDate = (input: Date | string): string => { - const date = new Date(input); - const hh = String(date.getUTCHours()).padStart(2, '0'); - const mm = String(date.getUTCMinutes()).padStart(2, '0'); - return `${hh}:${mm}`; -} - diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts index 6576477..86b9134 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -1,25 +1,7 @@ -import { sevenDaysFrom, toDateFromString, toHHmmFromDate, toStringFromDate } from "../helpers/timesheets-date-time-helpers"; import { Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; - -type TotalHours = { - regular: number; - evening: number; - emergency: number; - overtime: number; - vacation: number; - holiday: number; - sick: number; -}; - -type TotalExpenses = { - expenses: number; - per_diem: number; - on_call: number; - mileage: number; -}; - -const NUMBER_OF_TIMESHEETS_TO_RETURN = 2; +import { sevenDaysFrom, toStringFromDate, toHHmmFromDate, toDateFromString } from "src/time-and-attendance/utils/date-time.utils"; +import { TotalExpenses, TotalHours } from "src/time-and-attendance/utils/type.utils"; @Injectable() export class GetTimesheetsOverviewService { diff --git a/src/time-and-attendance/utils/constants.utils.ts b/src/time-and-attendance/utils/constants.utils.ts new file mode 100644 index 0000000..fe1e71f --- /dev/null +++ b/src/time-and-attendance/utils/constants.utils.ts @@ -0,0 +1,11 @@ +const NUMBER_OF_TIMESHEETS_TO_RETURN = 2; +const DAILY_LIMIT_HOURS = 8; +const WEEKLY_LIMIT_HOURS = 40; +const PAY_PERIOD_ANCHOR = 2023-12-17; +const PERIOD_DAYS = 14; +const PERIODS_PER_YEAR = 26; +const MS_PER_DAY = 86_400_000; + +//REGEX CONSTANTS +const DATE_ISO_FORMAT = /^\d{4}-\d{2}-\d{2}$/; +const HH_MM_REGEX = /^([01]\d|2[0-3]):[0-5]\d$/; diff --git a/src/time-and-attendance/utils/date-time-helpers.ts b/src/time-and-attendance/utils/date-time.utils.ts similarity index 65% rename from src/time-and-attendance/utils/date-time-helpers.ts rename to src/time-and-attendance/utils/date-time.utils.ts index 504a41b..74db965 100644 --- a/src/time-and-attendance/utils/date-time-helpers.ts +++ b/src/time-and-attendance/utils/date-time.utils.ts @@ -18,6 +18,8 @@ export const toStringFromHHmm = (date: Date): string => { export const toStringFromDate = (date: Date) => date.toISOString().slice(0,10); + + //converts HHmm format to string export const toHHmmFromString = (hhmm: string): Date => { const [hh, mm] = hhmm.split(':').map(Number); @@ -26,12 +28,28 @@ export const toHHmmFromString = (hhmm: string): Date => { return new Date(date); } +//converts string to HHmm format +export const toHHmmFromDate = (input: Date | string): string => { + const date = new Date(input); + const hh = String(date.getUTCHours()).padStart(2, '0'); + const mm = String(date.getUTCMinutes()).padStart(2, '0'); + return `${hh}:${mm}`; +} + //converts Date format to string -export const toDateFromString = (ymd: string): Date => { +export const toDateFromString = (ymd: string | Date): Date => { return new Date(`${ymd}T00:00:00:000Z`); } -export const toUTCDate = (iso: string | Date) => { +export const toUTCDateFromString = (iso: string | Date) => { const d = typeof iso === 'string' ? new Date(iso + 'T00:00:00.000Z') : iso; return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())); -}; \ No newline at end of file +}; + +export const sevenDaysFrom = (date: Date | string): Date[] => { + return Array.from({length: 7 }, (_,i) => { + const d = new Date(date); + d.setUTCDate(d.getUTCDate() + i ); + return d; + }); +} \ No newline at end of file diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/mappers/schedule-presets.mappers.ts b/src/time-and-attendance/utils/mappers.utils.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/mappers/schedule-presets.mappers.ts rename to src/time-and-attendance/utils/mappers.utils.ts diff --git a/src/time-and-attendance/utils/regex.constants.ts b/src/time-and-attendance/utils/regex.constants.ts deleted file mode 100644 index d192d70..0000000 --- a/src/time-and-attendance/utils/regex.constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -const DATE_ISO_FORMAT = /^\d{4}-\d{2}-\d{2}$/; -const HH_MM_REGEX = /^([01]\d|2[0-3]):[0-5]\d$/; \ No newline at end of file diff --git a/src/time-and-attendance/utils/selects.utils.ts b/src/time-and-attendance/utils/selects.utils.ts index 0140936..563769c 100644 --- a/src/time-and-attendance/utils/selects.utils.ts +++ b/src/time-and-attendance/utils/selects.utils.ts @@ -1,6 +1,5 @@ import { Prisma } from "@prisma/client"; - export const expense_select = { id: true, timesheet_id: true, @@ -44,5 +43,3 @@ export const leaveRequestsSelect = { }}, }}, } satisfies Prisma.LeaveRequestsSelect; - -export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>; \ No newline at end of file diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts new file mode 100644 index 0000000..e25b41c --- /dev/null +++ b/src/time-and-attendance/utils/type.utils.ts @@ -0,0 +1,64 @@ +import { WeekOvertimeSummary } from "src/time-and-attendance/domains/services/overtime.service"; +import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; +import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; +import { GetShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto"; +import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; +import { Prisma } from "@prisma/client"; + +export type TotalHours = { + regular: number; + evening: number; + emergency: number; + overtime: number; + vacation: number; + holiday: number; + sick: number; +}; + +export type TotalExpenses = { + expenses: number; + per_diem: number; + on_call: number; + mileage: number; +}; + +export type Normalized = { date: Date; start_time: Date; end_time: Date; }; + +export type ShiftWithOvertimeDto = { + shift: GetShiftDto; + overtime: WeekOvertimeSummary; +}; + +export type CreateResult = { ok: true; data: ShiftWithOvertimeDto } | { ok: false; error: any }; +export type UpdateChanges = Omit; +export type UpdatePayload = { id: number; dto: UpdateChanges }; +export type UpdateResult = { ok: true; id: number; data: ShiftWithOvertimeDto } | { ok: false; id: number; error: any }; +export type DeleteResult = { ok: true; id: number; overtime: WeekOvertimeSummary } | { ok: false; id: number; error: any }; + +export type NormedOk = { index: number; dto: ShiftDto; normed: Normalized }; +export type NormedErr = { index: number; error: any }; + + +export type ShiftResponse = { + week_day: string; + sort_order: number; + start_time: string; + end_time: string; + is_remote: boolean; + type: string; +}; + +export type PresetResponse = { + id: number; + name: string; + is_default: boolean; + shifts: ShiftResponse[]; +} + +export type ApplyResult = { + timesheet_id: number; + created: number; + skipped: number; +} + +export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>; From 7eb3844713e78bf51e728f4eb30c528db5a46dd6 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 27 Oct 2025 15:50:57 -0400 Subject: [PATCH 14/57] clean(time_and_attendance): cleaning helpers, types, mappers, constants --- .../services/pay-periods-query.service.ts | 2 +- .../modules/pay-period/utils/pay-year.util.ts | 35 --------- .../utils/constants.utils.ts | 1 + .../utils/date-time.utils.ts | 31 ++++++++ .../utils/selects.utils.ts | 74 ++++++++++--------- 5 files changed, 72 insertions(+), 71 deletions(-) delete mode 100644 src/time-and-attendance/modules/pay-period/utils/pay-year.util.ts diff --git a/src/time-and-attendance/modules/pay-period/services/pay-periods-query.service.ts b/src/time-and-attendance/modules/pay-period/services/pay-periods-query.service.ts index 0e6aac0..86ce75e 100644 --- a/src/time-and-attendance/modules/pay-period/services/pay-periods-query.service.ts +++ b/src/time-and-attendance/modules/pay-period/services/pay-periods-query.service.ts @@ -3,9 +3,9 @@ import { PrismaService } from "src/prisma/prisma.service"; import { computeHours } from "src/common/utils/date-utils"; import { PayPeriodOverviewDto } from "../dtos/overview-pay-period.dto"; import { EmployeePeriodOverviewDto } from "../dtos/overview-employee-period.dto"; -import { computePeriod, listPayYear, payYearOfDate } from "../utils/pay-year.util"; import { PayPeriodDto } from "../dtos/pay-period.dto"; import { mapPayPeriodToDto } from "../mappers/pay-periods.mapper"; +import { computePeriod, listPayYear, payYearOfDate } from "src/time-and-attendance/utils/date-time.utils"; @Injectable() export class PayPeriodsQueryService { diff --git a/src/time-and-attendance/modules/pay-period/utils/pay-year.util.ts b/src/time-and-attendance/modules/pay-period/utils/pay-year.util.ts deleted file mode 100644 index df45170..0000000 --- a/src/time-and-attendance/modules/pay-period/utils/pay-year.util.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { toStringFromDate, toUTCDateFromString } from "src/time-and-attendance/utils/date-time.utils"; - -export const ANCHOR_ISO = '2023-12-17'; // ancre date - - -export function payYearOfDate(date: string | Date, anchorISO = ANCHOR_ISO): number { - const ANCHOR = toUTCDateFromString(anchorISO); - const d = toUTCDateFromString(date); - const days = Math.floor((+d - +ANCHOR) / MS_PER_DAY); - const cycles = Math.floor(days / (PERIODS_PER_YEAR * PERIOD_DAYS)); - return ANCHOR.getUTCFullYear() + 1 + cycles; -} -//compute labels for periods -export function computePeriod(pay_year: number, period_no: number, anchorISO = ANCHOR_ISO) { - const ANCHOR = toUTCDateFromString(anchorISO); - const cycles = pay_year - (ANCHOR.getUTCFullYear() + 1); - const offsetPeriods = cycles * PERIODS_PER_YEAR + (period_no - 1); - const start = new Date(+ANCHOR + offsetPeriods * PERIOD_DAYS * MS_PER_DAY); - const end = new Date(+start + (PERIOD_DAYS - 1) * MS_PER_DAY); - const pay = new Date(end.getTime() + 6 * MS_PER_DAY); - return { - period_no: period_no, - pay_year: pay_year, - payday: toStringFromDate(pay), - period_start: toStringFromDate(start), - period_end: toStringFromDate(end), - label: `${toStringFromDate(start)}.${toStringFromDate(end)}`, - start, end, - }; -} - -//list of all 26 periods for a full year -export function listPayYear(pay_year: number, anchorISO = ANCHOR_ISO) { - return Array.from({ length: PERIODS_PER_YEAR }, (_, i) => computePeriod(pay_year, i + 1, anchorISO)); -} diff --git a/src/time-and-attendance/utils/constants.utils.ts b/src/time-and-attendance/utils/constants.utils.ts index fe1e71f..10318ce 100644 --- a/src/time-and-attendance/utils/constants.utils.ts +++ b/src/time-and-attendance/utils/constants.utils.ts @@ -2,6 +2,7 @@ const NUMBER_OF_TIMESHEETS_TO_RETURN = 2; const DAILY_LIMIT_HOURS = 8; const WEEKLY_LIMIT_HOURS = 40; const PAY_PERIOD_ANCHOR = 2023-12-17; +const ANCHOR_ISO = '2023-12-17'; // ancre date const PERIOD_DAYS = 14; const PERIODS_PER_YEAR = 26; const MS_PER_DAY = 86_400_000; diff --git a/src/time-and-attendance/utils/date-time.utils.ts b/src/time-and-attendance/utils/date-time.utils.ts index 74db965..4373fe7 100644 --- a/src/time-and-attendance/utils/date-time.utils.ts +++ b/src/time-and-attendance/utils/date-time.utils.ts @@ -52,4 +52,35 @@ export const sevenDaysFrom = (date: Date | string): Date[] => { d.setUTCDate(d.getUTCDate() + i ); return d; }); +} + +export function payYearOfDate(date: string | Date, anchorISO = ANCHOR_ISO): number { + const ANCHOR = toUTCDateFromString(anchorISO); + const d = toUTCDateFromString(date); + const days = Math.floor((+d - +ANCHOR) / MS_PER_DAY); + const cycles = Math.floor(days / (PERIODS_PER_YEAR * PERIOD_DAYS)); + return ANCHOR.getUTCFullYear() + 1 + cycles; +} +//compute labels for periods +export function computePeriod(pay_year: number, period_no: number, anchorISO = ANCHOR_ISO) { + const ANCHOR = toUTCDateFromString(anchorISO); + const cycles = pay_year - (ANCHOR.getUTCFullYear() + 1); + const offsetPeriods = cycles * PERIODS_PER_YEAR + (period_no - 1); + const start = new Date(+ANCHOR + offsetPeriods * PERIOD_DAYS * MS_PER_DAY); + const end = new Date(+start + (PERIOD_DAYS - 1) * MS_PER_DAY); + const pay = new Date(end.getTime() + 6 * MS_PER_DAY); + return { + period_no: period_no, + pay_year: pay_year, + payday: toStringFromDate(pay), + period_start: toStringFromDate(start), + period_end: toStringFromDate(end), + label: `${toStringFromDate(start)}.${toStringFromDate(end)}`, + start, end, + }; +} + +//list of all 26 periods for a full year +export function listPayYear(pay_year: number, anchorISO = ANCHOR_ISO) { + return Array.from({ length: PERIODS_PER_YEAR }, (_, i) => computePeriod(pay_year, i + 1, anchorISO)); } \ No newline at end of file diff --git a/src/time-and-attendance/utils/selects.utils.ts b/src/time-and-attendance/utils/selects.utils.ts index 563769c..3dbf127 100644 --- a/src/time-and-attendance/utils/selects.utils.ts +++ b/src/time-and-attendance/utils/selects.utils.ts @@ -1,45 +1,49 @@ import { Prisma } from "@prisma/client"; export const expense_select = { - id: true, - timesheet_id: true, - bank_code_id: true, - attachment: true, - date: true, - amount: true, - mileage: true, - comment: true, - supervisor_comment: true, - is_approved: true, + id: true, + timesheet_id: true, + bank_code_id: true, + attachment: true, + date: true, + amount: true, + mileage: true, + comment: true, + supervisor_comment: true, + is_approved: true, } satisfies Prisma.ExpensesSelect; export const shift_select = { - id: true, - timesheet_id: true, - bank_code_id: true, - date: true, - start_time: true, - end_time: true, - is_remote: true, - is_approved: true, - comment: true, + id: true, + timesheet_id: true, + bank_code_id: true, + date: true, + start_time: true, + end_time: true, + is_remote: true, + is_approved: true, + comment: true, } satisfies Prisma.ShiftsSelect; export const leaveRequestsSelect = { - id: true, - bank_code_id: true, - leave_type: true, - date: true, - payable_hours: true, - requested_hours: true, - comment: true, - approval_status: true, - employee: { select: { - id: true, - user: { select: { - email: true, - first_name: true, - last_name: true, - }}, - }}, + id: true, + bank_code_id: true, + leave_type: true, + date: true, + payable_hours: true, + requested_hours: true, + comment: true, + approval_status: true, + employee: { + select: { + id: true, + user: { + select: { + email: true, + first_name: true, + last_name: true, + }, + }, + } + }, } satisfies Prisma.LeaveRequestsSelect; From ca8c12ca6f018db2e3dce3ebbbc11ca187681077 Mon Sep 17 00:00:00 2001 From: Nicolas Drolet Date: Tue, 28 Oct 2025 14:45:06 -0400 Subject: [PATCH 15/57] feat(docker): add dockerfile for dockerization of backend in lab Should be in 10.100.251.2 --- Dockerfile | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..840a33b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +# Use the official Node.js image as the base image +FROM node:22 + +# Set the working directory inside the container +WORKDIR /usr/apptargo2/src/app + +# Copy package.json and package-lock.json to the working directory +COPY package*.json ./ + +# Install the application dependencies +RUN npm install + +# Copy the rest of the application files +COPY . . + +# Generate Prisma client +RUN npx prisma generate + +# Build the NestJS application +RUN npm run build + +# Expose the application port +EXPOSE 3000 + +# Command to run the application +CMD ["node", "dist/main"] From 2dd8bdb3c3b83a47582fd56c973c9dc2f1f77d8a Mon Sep 17 00:00:00 2001 From: Nicolas Drolet Date: Tue, 28 Oct 2025 15:34:20 -0400 Subject: [PATCH 16/57] fix(docker): Update workdir and CMD, add ENV variables for testing --- Dockerfile | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 840a33b..7063f1a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,27 @@ FROM node:22 # Set the working directory inside the container -WORKDIR /usr/apptargo2/src/app +WORKDIR /app + +# Set the environment variables +ENV DATABASE_URL_PROD="postgresql://apptargo:6wLAZrb0HZnd3mrmqXiArPcqLyui0o9e@10.100.0.116/app_targo_db?schema=public" +ENV DATABASE_URL_STAGING="postgresql://apptargo:6wLAZrb0HZnd3mrmqXiArPcqLyui0o9e@10.100.0.116/app_targo_db_staging?schema=public" +ENV DATABASE_URL_DEV="postgresql://apptargo:6wLAZrb0HZnd3mrmqXiArPcqLyui0o9e@10.100.0.116/app_targo_db_dev?schema=public" + +ENV AUTHENTIK_ISSUER="https://auth.targo.ca/application/o/montargo/" +ENV AUTHENTIK_CLIENT_ID="KUmSmvpu2aDDy4JfNwas7XriNFtPcj2Ka2PyLO5v" +ENV AUTHENTIK_CLIENT_SECRET="N55BgX1mxT7eiY99LOo5zXr5cKz9FgTsaCA9MdC7D8ZuhOGqozvqtNXVGbpY1eCg2kkYwJeJLP89sQ8R4cYybIJI7EwKijb19bzZQpUPwBosWwG3irUwdTnZOyw8yW5i" +ENV AUTHENTIK_CALLBACK_URL="http://10.100.251.2:3420/auth/callback" +ENV AUTHENTIK_AUTH_URL="https://auth.targo.ca/application/o/authorize/" +ENV AUTHENTIK_TOKEN_URL="https://auth.targo.ca/application/o/token/" +ENV AUTHENTIK_USERINFO_URL="https://auth.targo.ca/application/o/userinfo/" + +ENV TARGO_FRONTEND_URI="http://localhost:9000/" + +ENV ATTACHMENTS_SERVER_ID="server" +ENV ATTACHMENTS_ROOT=C:/ +ENV MAX_UPLOAD_MB=25 +ENV ALLOWED_MIME=image/jpeg,image/png,image/webp,application/pdf # Copy package.json and package-lock.json to the working directory COPY package*.json ./ @@ -23,4 +43,4 @@ RUN npm run build EXPOSE 3000 # Command to run the application -CMD ["node", "dist/main"] +CMD ["node", "dist/src/main"] From c0eef4e3a3595d1a2ee24ca0419375ae1d1ad6ca Mon Sep 17 00:00:00 2001 From: Nicolas Drolet Date: Wed, 29 Oct 2025 15:20:20 -0400 Subject: [PATCH 17/57] feat(docker): Add/Update Dockerfile for Remote Docker Lab deployment --- Dockerfile | 4 +++- src/main.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7063f1a..21299ad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,9 @@ ENV AUTHENTIK_AUTH_URL="https://auth.targo.ca/application/o/authorize/" ENV AUTHENTIK_TOKEN_URL="https://auth.targo.ca/application/o/token/" ENV AUTHENTIK_USERINFO_URL="https://auth.targo.ca/application/o/userinfo/" -ENV TARGO_FRONTEND_URI="http://localhost:9000/" +ENV TARGO_FRONTEND_URI_1="http://targo-frontend-nicolas:9000" +ENV TARGO_FRONTEND_URI_2="http://targo-frontend-matthieu:9000" +ENV TARGO_FRONTEND_URI_3="http://targo-frontend-lion:9000" ENV ATTACHMENTS_SERVER_ID="server" ENV ATTACHMENTS_ROOT=C:/ diff --git a/src/main.ts b/src/main.ts index 88237b6..89eb564 100644 --- a/src/main.ts +++ b/src/main.ts @@ -46,7 +46,7 @@ async function bootstrap() { // Enable CORS app.enableCors({ - origin: 'http://localhost:9000', + origin: [process.env.TARGO_FRONTEND_URI_1, process.env.TARGO_FRONTEND_URI_2, process.env.TARGO_FRONTEND_URI_3], credentials: true, }); From 50521b7c681940eb684020666fdfd8742ab37c78 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Thu, 30 Oct 2025 12:04:05 -0400 Subject: [PATCH 18/57] refactor(presets): removed upsertAction manipulation. created 3 seperate routes for create update et delete. --- .../domains/services/mileage.service.ts | 3 +- .../modules/shared/shared.module.ts | 6 +- .../shared/types/upsert-actions.types.ts | 1 - .../controller/schedule-presets.controller.ts | 57 +++-- .../dtos/create-schedule-preset-shifts.dto.ts | 4 + .../dtos/update-schedule-presets.dto.ts | 3 + .../schedule-presets.module.ts | 11 +- .../schedule-presets-apply.service.ts | 8 +- .../schedule-presets-command.service.ts | 206 ++++++++---------- .../schedule-presets-query.service.ts | 2 +- .../shifts/controllers/shift.controller.ts | 2 +- .../shifts/dtos/shift-update.dto.ts | 2 +- .../services/shifts-archival.service.ts | 4 +- .../shifts/services/shifts-get.service.ts | 4 +- .../shifts/services/shifts-upsert.service.ts | 7 +- .../utils/constants.utils.ts | 20 +- .../utils/date-time.utils.ts | 5 +- src/time-and-attendance/utils/type.utils.ts | 2 + 18 files changed, 175 insertions(+), 172 deletions(-) delete mode 100644 src/time-and-attendance/modules/shared/types/upsert-actions.types.ts create mode 100644 src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts diff --git a/src/time-and-attendance/domains/services/mileage.service.ts b/src/time-and-attendance/domains/services/mileage.service.ts index 8919532..030ce42 100644 --- a/src/time-and-attendance/domains/services/mileage.service.ts +++ b/src/time-and-attendance/domains/services/mileage.service.ts @@ -1,6 +1,5 @@ import { BadRequestException, Injectable, Logger } from "@nestjs/common"; -import { PrismaService } from "../../../prisma/prisma.service"; -import { Decimal } from "@prisma/client/runtime/library"; +import { PrismaService } from '../../../prisma/prisma.service'; @Injectable() export class MileageService { diff --git a/src/time-and-attendance/modules/shared/shared.module.ts b/src/time-and-attendance/modules/shared/shared.module.ts index 8d4aa95..0e26d7b 100644 --- a/src/time-and-attendance/modules/shared/shared.module.ts +++ b/src/time-and-attendance/modules/shared/shared.module.ts @@ -1,9 +1,9 @@ -import { Module } from "@nestjs/common"; -import { EmailToIdResolver } from "./utils/resolve-email-id.utils"; import { EmployeeTimesheetResolver } from "./utils/resolve-timesheet.utils"; -import { FullNameResolver } from "./utils/resolve-full-name.utils"; +import { EmailToIdResolver } from "./utils/resolve-email-id.utils"; import { BankCodesResolver } from "./utils/resolve-bank-type-id.utils"; +import { FullNameResolver } from "./utils/resolve-full-name.utils"; import { PrismaModule } from "src/prisma/prisma.module"; +import { Module } from "@nestjs/common"; @Module({ imports: [PrismaModule], diff --git a/src/time-and-attendance/modules/shared/types/upsert-actions.types.ts b/src/time-and-attendance/modules/shared/types/upsert-actions.types.ts deleted file mode 100644 index 9342d75..0000000 --- a/src/time-and-attendance/modules/shared/types/upsert-actions.types.ts +++ /dev/null @@ -1 +0,0 @@ -export type UpsertAction = 'create' | 'update' | 'delete'; \ No newline at end of file diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index f02b958..965e43b 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -1,44 +1,63 @@ -import { BadRequestException, Body, Controller, Get, NotFoundException, Param, Post, Put, Query } from "@nestjs/common"; -import { SchedulePresetsCommandService } from "../services/schedule-presets-command.service"; -import { SchedulePresetsQueryService } from "../services/schedule-presets-query.service"; -import { SchedulePresetsDto } from "../dtos/create-schedule-presets.dto"; -import { UpsertAction } from "src/time-and-attendance/modules/shared/types/upsert-actions.types"; +import { Controller, Put, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch } from "@nestjs/common"; +import { SchedulePresetsCommandService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service"; +import { SchedulePresetsApplyService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service"; +import { SchedulePresetsQueryService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service"; +import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; +import { SchedulePresetsUpdateDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto"; + + @Controller('schedule-presets') export class SchedulePresetsController { constructor( private readonly commandService: SchedulePresetsCommandService, + private readonly applyPresetsService: SchedulePresetsApplyService, private readonly queryService: SchedulePresetsQueryService, ){} - //used to create, update or delete a schedule preset - @Put(':email') - async upsert( - @Param('email') email: string, - @Query('action') action: UpsertAction, - @Body() dto: SchedulePresetsDto, + //used to create a schedule preset + @Post(':employee_id') + async createPreset( + @Param('employee_id', ParseIntPipe) employee_id: number, + @Body() dto: SchedulePresetsDto, ) { - const actions: UpsertAction[] = ['create','update','delete']; - if(!actions) throw new NotFoundException(`No action found for ${actions}`) - return this.commandService.upsertSchedulePreset(email, action, dto); + return await this.commandService.createPreset(employee_id, dto); } + //used to update an already existing schedule preset + @Patch(':preset_id') + async updatePreset( + @Param('preset_id', ParseIntPipe) preset_id: number, + @Body() dto: SchedulePresetsUpdateDto, + ) { + return await this.commandService.updatePreset(preset_id, dto); + } + + //used to delete a schedule preset + @Delete(':preset_id') + async deletePreset( + @Param('preset_id') preset_id: number, + ) { + return await this.commandService.deletePreset(preset_id); + } + + //used to show the list of available schedule presets @Get(':email') async findListByEmail( - @Param('email') email: string, + @Param('email') email: string, ) { return this.queryService.findSchedulePresetsByEmail(email); } //used to apply a preset to a timesheet @Post('/apply-presets/:email') async applyPresets( - @Param('email') email: string, - @Query('preset') preset_name: string, - @Query('start') start_date: string, + @Param('email') email: string, + @Query('preset') preset_name: string, + @Query('start') start_date: string, ) { if(!preset_name?.trim()) throw new BadRequestException('Query "preset" is required'); if(!start_date?.trim()) throw new BadRequestException('Query "start" is required YYYY-MM-DD'); - return this.applyPresets(email, preset_name, start_date); + return this.applyPresetsService.applyToTimesheet(email, preset_name, start_date); } } \ No newline at end of file diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts index 33c06cd..5e37dcb 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts @@ -1,10 +1,14 @@ import { IsBoolean, IsEnum, IsInt, IsOptional, IsString, Matches, Min } from "class-validator"; +import { HH_MM_REGEX } from "src/time-and-attendance/utils/constants.utils"; import { Weekday } from "@prisma/client"; export class SchedulePresetShiftsDto { @IsEnum(Weekday) week_day!: Weekday; + @IsInt() + preset_id!: number; + @IsInt() @Min(1) sort_order!: number; diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts new file mode 100644 index 0000000..2246c20 --- /dev/null +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts @@ -0,0 +1,3 @@ +import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; + +export class SchedulePresetsUpdateDto extends SchedulePresetsDto{} \ No newline at end of file diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts index 70d34f5..1a8f9bf 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts @@ -1,9 +1,10 @@ -import { Module } from "@nestjs/common"; -import { SchedulePresetsCommandService } from "./services/schedule-presets-command.service"; -import { SchedulePresetsQueryService } from "./services/schedule-presets-query.service"; -import { SchedulePresetsController } from "./controller/schedule-presets.controller"; -import { SchedulePresetsApplyService } from "./services/schedule-presets-apply.service"; +import { SchedulePresetsCommandService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service"; +import { SchedulePresetsApplyService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service"; +import { SchedulePresetsQueryService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service"; +import { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller"; import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; +import { Module } from "@nestjs/common"; + @Module({ imports: [SharedModule], diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts index 7bad7f6..3d7d0f1 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts @@ -1,9 +1,11 @@ -import { BadRequestException, ConflictException, Injectable, NotFoundException } from "@nestjs/common"; +import { Injectable, BadRequestException, NotFoundException, ConflictException } from "@nestjs/common"; import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { Weekday, Prisma } from "@prisma/client"; +import { DATE_ISO_FORMAT } from "src/time-and-attendance/utils/constants.utils"; import { PrismaService } from "src/prisma/prisma.service"; -import { Prisma, Weekday } from "@prisma/client"; -import { WEEKDAY } from "../../../../utils/mappers.utils"; import { ApplyResult } from "src/time-and-attendance/utils/type.utils"; +import { WEEKDAY } from "src/time-and-attendance/utils/mappers.utils"; + @Injectable() export class SchedulePresetsApplyService { diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts index 3803004..cb35378 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts @@ -1,73 +1,31 @@ -import { BadRequestException, ConflictException, Injectable, NotFoundException } from "@nestjs/common"; -import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; -import { UpsertAction } from "src/time-and-attendance/modules/shared/types/upsert-actions.types"; -import { PrismaService } from "src/prisma/prisma.service"; -import { SchedulePresetsDto } from "../dtos/create-schedule-presets.dto"; -import { Prisma, Weekday } from "@prisma/client"; +import { Injectable, BadRequestException, NotFoundException, ConflictException } from "@nestjs/common"; +import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; +import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; +import { Prisma, Weekday } from "@prisma/client"; +import { PrismaService } from "src/prisma/prisma.service"; +import { toHHmmFromDate } from "src/time-and-attendance/utils/date-time.utils"; + +type DeleteResult = { ok: true; id: number; } | { ok: false; id: number; error: any }; +type CreateResult = { ok: true; } | { ok: false; error: any }; +type UpdateResult = { ok: true; id: number; data: SchedulePresetsDto } | { ok: false; id: number; error: any }; @Injectable() export class SchedulePresetsCommandService { constructor( private readonly prisma: PrismaService, - private readonly emailResolver: EmailToIdResolver, private readonly typeResolver : BankCodesResolver, - ){} - - //_________________________________________________________________ - // MASTER CRUD FUNCTION - //_________________________________________________________________ - async upsertSchedulePreset( - email: string, - action: UpsertAction, - dto: SchedulePresetsDto, - ): Promise<{ - action: UpsertAction; - preset_id?: number; - total_items?: number; - }>{ - if(!dto.name?.trim()) throw new BadRequestException(`A Name is required`); - - //resolve employee_id using email - const employee_id = await this.emailResolver.findIdByEmail(email); - if(!employee_id) throw new NotFoundException(`employee with email: ${email} not found`); - - //DELETE - if(action === 'delete') { - return this.deletePreset(employee_id, dto.name); - } - - if(!Array.isArray(dto.preset_shifts) || dto.preset_shifts.length === 0) { - throw new BadRequestException(`Empty array, no detected shifts`); - } - const shifts_data = await this.resolveAndBuildPresetShifts(dto); - - //CREATE AND UPDATE - if(action === 'create') { - return this.createPreset(employee_id, dto, shifts_data); - } else if (action === 'update') { - return this.updatePreset(employee_id, dto, shifts_data); - } - throw new BadRequestException(`Unknown action: ${ action }`); - } - + ){} //_________________________________________________________________ // CREATE //_________________________________________________________________ - private async createPreset( - employee_id: number, - dto: SchedulePresetsDto, - shifts_data: Prisma.SchedulePresetShiftsCreateWithoutPresetInput[], - ): Promise<{ - action: UpsertAction; - preset_id: number; - total_items: number; - }> { + async createPreset( employee_id: number, dto: SchedulePresetsDto): Promise { try { - const result = await this.prisma.$transaction(async (tx)=> { + const shifts_data = await this.resolveAndBuildPresetShifts(dto); + if(!shifts_data) throw new BadRequestException(`Employee with id: ${employee_id} or dto not found`); + await this.prisma.$transaction(async (tx)=> { if(dto.is_default) { await tx.schedulePresets.updateMany({ - where: { employee_id, is_default: true }, + where: { is_default: true, employee_id }, data: { is_default: false }, }); } @@ -78,56 +36,53 @@ export class SchedulePresetsCommandService { is_default: !!dto.is_default, shifts: { create: shifts_data}, }, - include: { shifts: true }, }); return created; }); - return { action: 'create', preset_id: result.id, total_items: result.shifts.length }; + return { ok: true }; + } catch (error: unknown) { - if(error instanceof Prisma.PrismaClientKnownRequestError){ - if(error?.code === 'P2002') { - throw new ConflictException(`The name ${dto.name} is already used for another schedule preset`); - } - if (error.code === 'P2003' || error.code === 'P2011') { - throw new ConflictException('Invalid constraint on preset shifts'); - } - } - throw error; + return { ok: false, error }; } } //_________________________________________________________________ // UPDATE //_________________________________________________________________ - private async updatePreset( - employee_id: number, - dto: SchedulePresetsDto, - shifts_data: Prisma.SchedulePresetShiftsCreateWithoutPresetInput[], - ): Promise<{ - action: UpsertAction; - preset_id?: number; - total_items?: number; - }> { - const existing = await this.prisma.schedulePresets.findFirst({ - where: { employee_id, name: dto.name }, - select: { id:true, is_default: true }, - }); - if(!existing) throw new NotFoundException(`Preset "${dto.name}" not found`); - + async updatePreset( preset_id: number, dto: SchedulePresetsDto ): Promise { try { - const result = await this.prisma.$transaction(async (tx) => { + const existing = await this.prisma.schedulePresets.findFirst({ + where: { id: preset_id }, + select: { + id:true, + is_default: true, + employee_id: true, + }, + }); + if(!existing) throw new NotFoundException(`Preset "${dto.name}" not found`); + + const shifts_data = await this.resolveAndBuildPresetShifts(dto); + await this.prisma.$transaction(async (tx) => { if(typeof dto.is_default === 'boolean'){ if(dto.is_default) { await tx.schedulePresets.updateMany({ - where: { employee_id, is_default: true, NOT: { id: existing.id } }, + where: { + employee_id: existing.employee_id, + is_default: true, + NOT: { id: existing.id }, + }, data: { is_default: false }, }); } await tx.schedulePresets.update({ where: { id: existing.id }, - data: { is_default: dto.is_default }, + data: { + is_default: dto.is_default, + name: dto.name, + }, }); } + if(shifts_data.length <= 0) throw new BadRequestException('Preset shifts to update not found'); await tx.schedulePresetShifts.deleteMany({ where: { preset_id: existing.id } }); @@ -148,45 +103,62 @@ export class SchedulePresetsCommandService { }; }); await tx.schedulePresetShifts.createMany({data: create_many_data}); - - const count = await tx.schedulePresetShifts.count({ where: { preset_id: existing.id } }); - return { id: existing.id, total: count }; }); - return { action: 'update', preset_id: result.id, total_items: result.total }; - } catch (error: unknown){ - if(error instanceof Prisma.PrismaClientKnownRequestError){ - if(error?.code === 'P2003' || error?.code === 'P2011') { - throw new ConflictException(`Invalid constraint on preset shifts`); - } - } - throw error; + + const saved = await this.prisma.schedulePresets.findUnique({ + where: { id: existing.id }, + include: { shifts: { + orderBy: [{ week_day: 'asc' }, { sort_order: 'asc' }], + include: { bank_code: { select: { type: true }}}, + }}, + }); + if(!saved) throw new NotFoundException(`Preset with id: ${existing.id} not found`); + + const response_dto: SchedulePresetsDto = { + name: saved.name, + is_default: saved.is_default, + preset_shifts: saved.shifts.map((shift) => ({ + preset_id: shift.preset_id, + week_day: shift.week_day, + sort_order: shift.sort_order, + type: shift.bank_code.type, + start_time: toHHmmFromDate(shift.start_time), + end_time: toHHmmFromDate(shift.end_time), + is_remote: shift.is_remote, + })), + }; + + return { ok: true, id: existing.id, data: response_dto }; + } catch (error){ + return { ok: false, id: preset_id, error } } } //_________________________________________________________________ // DELETE //_________________________________________________________________ - private async deletePreset( - employee_id: number, - name: string, - ): Promise<{ - action: UpsertAction; - preset_id?: number; - total_items?: number; - }> { - const existing = await this.prisma.schedulePresets.findFirst({ - where: { employee_id, name }, - select: { id: true }, - }); - if(!existing) throw new NotFoundException(`Preset "${name}" not found`); - await this.prisma.$transaction(async (tx) => { - await tx.schedulePresetShifts.deleteMany({ where: { preset_id: existing.id } }); - await tx.schedulePresets.delete({where: { id: existing.id } }); - }); - return { action: 'delete', preset_id: existing.id, total_items: 0 }; + async deletePreset( preset_id: number ): Promise { + try { + await this.prisma.$transaction(async (tx) => { + const preset = await tx.schedulePresets.findFirst({ + where: { id: preset_id }, + select: { id: true }, + }); + if(!preset) throw new NotFoundException(`Preset with id ${ preset_id } not found`); + await tx.schedulePresets.delete({where: { id: preset_id } }); + + return { success: true }; + }); + return { ok: true, id: preset_id }; + + } catch (error) { + if(error) throw new NotFoundException(`Preset schedule with id ${ preset_id } not found`); + return { ok: false, id: preset_id, error }; + } } - //PRIVATE HELPER + //PRIVATE HELPERS + //resolve bank_code_id using type and convert hours to TIME and valid shifts end/start private async resolveAndBuildPresetShifts( dto: SchedulePresetsDto diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts index 74d464b..ec0d134 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts @@ -1,8 +1,8 @@ import { Injectable, NotFoundException } from "@nestjs/common"; +import { PresetResponse, ShiftResponse } from "src/time-and-attendance/utils/type.utils"; import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { Prisma } from "@prisma/client"; -import { PresetResponse, ShiftResponse } from "src/time-and-attendance/utils/type.utils"; @Injectable() export class SchedulePresetsQueryService { diff --git a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts index 1658e3a..6c4968c 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts @@ -1,8 +1,8 @@ import { BadRequestException, Body, Controller, Delete, Param, Patch, Post } from "@nestjs/common"; import { CreateResult, UpdateResult } from "src/time-and-attendance/utils/type.utils"; import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; -import { ShiftDto } from "../dtos/shift-create.dto"; import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; +import { ShiftDto } from "../dtos/shift-create.dto"; @Controller('shift') diff --git a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts b/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts index a9f2901..99645cb 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts @@ -1,6 +1,6 @@ import { PartialType, OmitType } from "@nestjs/swagger"; -import { IsInt } from "class-validator"; import { ShiftDto } from "./shift-create.dto"; +import { IsInt } from "class-validator"; export class UpdateShiftDto extends PartialType( // allows update using ShiftDto and preventing OmitType variables to be modified diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts index 6f60390..a5a833f 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts @@ -1,6 +1,6 @@ -import { Injectable } from "@nestjs/common"; -import { ShiftsArchive } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; +import { ShiftsArchive } from "@prisma/client"; +import { Injectable } from "@nestjs/common"; /** * _____________________________________________________________________________________ diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts index 67f887a..8971cf8 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts @@ -1,8 +1,8 @@ +import { toStringFromDate, toStringFromHHmm } from "../../../../utils/date-time.utils"; import { Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; -import { GetShiftDto } from "../dtos/shift-get.dto"; -import { toStringFromDate, toStringFromHHmm } from "../../../../utils/date-time.utils"; import { shift_select } from "src/time-and-attendance/utils/selects.utils"; +import { GetShiftDto } from "../dtos/shift-get.dto"; /** * _____________________________________________________________________________________ diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts index e9c18d8..cca5e5a 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts @@ -1,15 +1,14 @@ import { CreateResult, NormedOk, NormedErr, UpdatePayload, UpdateResult, Normalized, UpdateChanges } from "src/time-and-attendance/utils/type.utils"; -import { toDateFromString, toHHmmFromString, toStringFromDate, toStringFromHHmm } from "../../../../utils/date-time.utils"; +import { overlaps, toDateFromString, toHHmmFromString, toStringFromDate, toStringFromHHmm } from "../../../../utils/date-time.utils"; import { BadRequestException, ConflictException, Injectable, NotFoundException } from "@nestjs/common"; import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; +import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; import { PrismaService } from "src/prisma/prisma.service"; import { shift_select } from "src/time-and-attendance/utils/selects.utils"; import { GetShiftDto } from "../dtos/shift-get.dto"; import { ShiftDto } from "../dtos/shift-create.dto"; -import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; -const overlaps = (a: { start: Date; end: Date }, b: { start: Date; end: Date }) => - !(a.end <= b.start || a.start >= b.end); + @Injectable() export class ShiftsUpsertService { diff --git a/src/time-and-attendance/utils/constants.utils.ts b/src/time-and-attendance/utils/constants.utils.ts index 10318ce..c02df87 100644 --- a/src/time-and-attendance/utils/constants.utils.ts +++ b/src/time-and-attendance/utils/constants.utils.ts @@ -1,12 +1,12 @@ -const NUMBER_OF_TIMESHEETS_TO_RETURN = 2; -const DAILY_LIMIT_HOURS = 8; -const WEEKLY_LIMIT_HOURS = 40; -const PAY_PERIOD_ANCHOR = 2023-12-17; -const ANCHOR_ISO = '2023-12-17'; // ancre date -const PERIOD_DAYS = 14; -const PERIODS_PER_YEAR = 26; -const MS_PER_DAY = 86_400_000; +export const NUMBER_OF_TIMESHEETS_TO_RETURN = 2; +export const DAILY_LIMIT_HOURS = 8; +export const WEEKLY_LIMIT_HOURS = 40; +export const PAY_PERIOD_ANCHOR = 2023-12-17; +export const ANCHOR_ISO = '2023-12-17'; // ancre date +export const PERIOD_DAYS = 14; +export const PERIODS_PER_YEAR = 26; +export const MS_PER_DAY = 86_400_000; //REGEX CONSTANTS -const DATE_ISO_FORMAT = /^\d{4}-\d{2}-\d{2}$/; -const HH_MM_REGEX = /^([01]\d|2[0-3]):[0-5]\d$/; +export const DATE_ISO_FORMAT = /^\d{4}-\d{2}-\d{2}$/; +export const HH_MM_REGEX = /^([01]\d|2[0-3]):[0-5]\d$/; diff --git a/src/time-and-attendance/utils/date-time.utils.ts b/src/time-and-attendance/utils/date-time.utils.ts index 4373fe7..a37dc24 100644 --- a/src/time-and-attendance/utils/date-time.utils.ts +++ b/src/time-and-attendance/utils/date-time.utils.ts @@ -83,4 +83,7 @@ export function computePeriod(pay_year: number, period_no: number, anchorISO = A //list of all 26 periods for a full year export function listPayYear(pay_year: number, anchorISO = ANCHOR_ISO) { return Array.from({ length: PERIODS_PER_YEAR }, (_, i) => computePeriod(pay_year, i + 1, anchorISO)); -} \ No newline at end of file +} + +export const overlaps = (a: { start: Date; end: Date }, b: { start: Date; end: Date }) => + !(a.end <= b.start || a.start >= b.end); \ No newline at end of file diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts index e25b41c..0903b87 100644 --- a/src/time-and-attendance/utils/type.utils.ts +++ b/src/time-and-attendance/utils/type.utils.ts @@ -62,3 +62,5 @@ export type ApplyResult = { } export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>; + +export type UpsertAction = 'create' | 'update' | 'delete'; \ No newline at end of file From 1385777122780730eac8419aa15614ad8cd08829 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Thu, 30 Oct 2025 12:22:12 -0400 Subject: [PATCH 19/57] refactor(presets): modified routes name and switch Param(email) to use employee_id instead. ajusted these methods to use employee_id accordingly --- .../controller/schedule-presets.controller.ts | 30 +++++++++---------- .../schedule-presets-apply.service.ts | 11 ++----- .../schedule-presets-query.service.ts | 13 ++------ 3 files changed, 20 insertions(+), 34 deletions(-) diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index 965e43b..bd05224 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -1,11 +1,9 @@ -import { Controller, Put, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch } from "@nestjs/common"; +import { Controller, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch } from "@nestjs/common"; import { SchedulePresetsCommandService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service"; import { SchedulePresetsApplyService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service"; import { SchedulePresetsQueryService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service"; -import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; import { SchedulePresetsUpdateDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto"; - - +import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; @Controller('schedule-presets') export class SchedulePresetsController { @@ -16,7 +14,7 @@ export class SchedulePresetsController { ){} //used to create a schedule preset - @Post(':employee_id') + @Post('create/:employee_id') async createPreset( @Param('employee_id', ParseIntPipe) employee_id: number, @Body() dto: SchedulePresetsDto, @@ -25,7 +23,7 @@ export class SchedulePresetsController { } //used to update an already existing schedule preset - @Patch(':preset_id') + @Patch('update/:preset_id') async updatePreset( @Param('preset_id', ParseIntPipe) preset_id: number, @Body() dto: SchedulePresetsUpdateDto, @@ -34,7 +32,7 @@ export class SchedulePresetsController { } //used to delete a schedule preset - @Delete(':preset_id') + @Delete('delete/:preset_id') async deletePreset( @Param('preset_id') preset_id: number, ) { @@ -43,21 +41,23 @@ export class SchedulePresetsController { //used to show the list of available schedule presets - @Get(':email') - async findListByEmail( - @Param('email') email: string, + @Get('find/:employee_id') + async findListById( + @Param('employee_id', ParseIntPipe) employee_id: number, ) { - return this.queryService.findSchedulePresetsByEmail(email); + return this.queryService.findSchedulePresets(employee_id); } + + //used to apply a preset to a timesheet - @Post('/apply-presets/:email') + @Post('/apply-presets/:employee_id') async applyPresets( - @Param('email') email: string, + @Param('employee_id') employee_id: number, @Query('preset') preset_name: string, - @Query('start') start_date: string, + @Query('start') start_date: string, ) { if(!preset_name?.trim()) throw new BadRequestException('Query "preset" is required'); if(!start_date?.trim()) throw new BadRequestException('Query "start" is required YYYY-MM-DD'); - return this.applyPresetsService.applyToTimesheet(email, preset_name, start_date); + return this.applyPresetsService.applyToTimesheet(employee_id, preset_name, start_date); } } \ No newline at end of file diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts index 3d7d0f1..75bdf1c 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts @@ -1,5 +1,4 @@ import { Injectable, BadRequestException, NotFoundException, ConflictException } from "@nestjs/common"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; import { Weekday, Prisma } from "@prisma/client"; import { DATE_ISO_FORMAT } from "src/time-and-attendance/utils/constants.utils"; import { PrismaService } from "src/prisma/prisma.service"; @@ -9,22 +8,16 @@ import { WEEKDAY } from "src/time-and-attendance/utils/mappers.utils"; @Injectable() export class SchedulePresetsApplyService { - constructor( - private readonly prisma: PrismaService, - private readonly emailResolver: EmailToIdResolver, - ) {} + constructor( private readonly prisma: PrismaService) {} async applyToTimesheet( - email: string, + employee_id: number, preset_name: string, start_date_iso: string, ): Promise { if(!preset_name?.trim()) throw new BadRequestException('A preset_name is required'); if(!DATE_ISO_FORMAT.test(start_date_iso)) throw new BadRequestException('start_date must be of format :YYYY-MM-DD'); - const employee_id = await this.emailResolver.findIdByEmail(email); - if(!employee_id) throw new NotFoundException(`Employee with email: ${email} not found`); - const preset = await this.prisma.schedulePresets.findFirst({ where: { employee_id, name: preset_name }, include: { diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts index ec0d134..b16911c 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts @@ -1,20 +1,13 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; import { PresetResponse, ShiftResponse } from "src/time-and-attendance/utils/type.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; import { PrismaService } from "src/prisma/prisma.service"; +import { Injectable } from "@nestjs/common"; import { Prisma } from "@prisma/client"; @Injectable() export class SchedulePresetsQueryService { - constructor( - private readonly prisma: PrismaService, - private readonly emailResolver: EmailToIdResolver, - ){} - - async findSchedulePresetsByEmail(email:string): Promise { - const employee_id = await this.emailResolver.findIdByEmail(email); - if(!employee_id) throw new NotFoundException(`Employee with email: ${email} not found`); + constructor( private readonly prisma: PrismaService ){} + async findSchedulePresets(employee_id: number): Promise { try { const presets = await this.prisma.schedulePresets.findMany({ where: { employee_id }, From 2debd408712aab9bda3fc0f10bea1ca65684b175 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Thu, 30 Oct 2025 12:24:24 -0400 Subject: [PATCH 20/57] fix(imports): fix constants imports --- docs/swagger/swagger-spec.json | 96 +++++++++++++++---- .../utils/leave-request.util.ts | 6 -- .../timesheet-get-overview.service.ts | 1 + .../utils/date-time.utils.ts | 2 + 4 files changed, 78 insertions(+), 27 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 4167a3f..e9c9b27 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -454,24 +454,16 @@ ] } }, - "/schedule-presets/{email}": { - "put": { - "operationId": "SchedulePresetsController_upsert", + "/schedule-presets/create/{employee_id}": { + "post": { + "operationId": "SchedulePresetsController_createPreset", "parameters": [ { - "name": "email", + "name": "employee_id", "required": true, "in": "path", "schema": { - "type": "string" - } - }, - { - "name": "action", - "required": true, - "in": "query", - "schema": { - "type": "string" + "type": "number" } } ], @@ -485,6 +477,39 @@ } } }, + "responses": { + "201": { + "description": "" + } + }, + "tags": [ + "SchedulePresets" + ] + } + }, + "/schedule-presets/update/{preset_id}": { + "patch": { + "operationId": "SchedulePresetsController_updatePreset", + "parameters": [ + { + "name": "preset_id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SchedulePresetsUpdateDto" + } + } + } + }, "responses": { "200": { "description": "" @@ -493,16 +518,18 @@ "tags": [ "SchedulePresets" ] - }, - "get": { - "operationId": "SchedulePresetsController_findListByEmail", + } + }, + "/schedule-presets/delete/{preset_id}": { + "delete": { + "operationId": "SchedulePresetsController_deletePreset", "parameters": [ { - "name": "email", + "name": "preset_id", "required": true, "in": "path", "schema": { - "type": "string" + "type": "number" } } ], @@ -516,16 +543,39 @@ ] } }, - "/schedule-presets/apply-presets/{email}": { + "/schedule-presets/find/{employee_id}": { + "get": { + "operationId": "SchedulePresetsController_findListById", + "parameters": [ + { + "name": "employee_id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "SchedulePresets" + ] + } + }, + "/schedule-presets/apply-presets/{employee_id}": { "post": { "operationId": "SchedulePresetsController_applyPresets", "parameters": [ { - "name": "email", + "name": "employee_id", "required": true, "in": "path", "schema": { - "type": "string" + "type": "number" } }, { @@ -853,6 +903,10 @@ "type": "object", "properties": {} }, + "SchedulePresetsUpdateDto": { + "type": "object", + "properties": {} + }, "ExpenseDto": { "type": "object", "properties": {} diff --git a/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts b/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts index 7c66afb..1fdceaa 100644 --- a/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts +++ b/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts @@ -2,16 +2,11 @@ import { BadRequestException, Injectable } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { LeaveTypes } from "@prisma/client"; -// import { toDateOnly, toStringFromDate } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; -import { UpsertAction } from "src/time-and-attendance/modules/shared/types/upsert-actions.types"; import { toDateFromString, toStringFromDate } from "src/time-and-attendance/utils/date-time.utils"; -// import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; - @Injectable() export class LeaveRequestsUtils { constructor( private readonly prisma: PrismaService, - // private readonly shiftsService: ShiftsUpsertService, ){} async syncShift( @@ -47,7 +42,6 @@ export class LeaveRequestsUtils { include: { bank_code: true }, }); - const action: UpsertAction = existing ? 'update' : 'create'; // await this.shiftsService.upsertShifts(email, action, { // old_shift: existing diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts index 86b9134..24aa158 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -1,5 +1,6 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; +import { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/time-and-attendance/utils/constants.utils"; import { sevenDaysFrom, toStringFromDate, toHHmmFromDate, toDateFromString } from "src/time-and-attendance/utils/date-time.utils"; import { TotalExpenses, TotalHours } from "src/time-and-attendance/utils/type.utils"; diff --git a/src/time-and-attendance/utils/date-time.utils.ts b/src/time-and-attendance/utils/date-time.utils.ts index a37dc24..c43337d 100644 --- a/src/time-and-attendance/utils/date-time.utils.ts +++ b/src/time-and-attendance/utils/date-time.utils.ts @@ -1,3 +1,5 @@ +import { ANCHOR_ISO, MS_PER_DAY, PERIODS_PER_YEAR, PERIOD_DAYS } from "src/time-and-attendance/utils/constants.utils"; + //ensures the week starts from sunday export function weekStartSunday(date_local: Date): Date { const start = new Date(Date.UTC(date_local.getFullYear(), date_local.getMonth(), date_local.getDate())); From 2b033de91b7a2e7cd47efe1705be7e8a8b0266ce Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Thu, 30 Oct 2025 13:57:16 -0400 Subject: [PATCH 21/57] fix(imports): ajusted imports to remove relative paths --- .../controllers/expense.controller.ts | 11 ++--- .../expenses/dtos/expense-update.dto.ts | 2 +- .../services/expense-upsert.service.ts | 41 ++++--------------- .../services/expenses-archival.service.ts | 2 +- .../controller/schedule-presets.controller.ts | 16 ++++---- .../dtos/create-schedule-presets.dto.ts | 2 +- .../schedule-presets.module.ts | 12 +++--- ...ice.ts => schedule-presets-get.service.ts} | 4 +- ....ts => schedule-presets-upsert.service.ts} | 15 +++---- .../shifts/controllers/shift.controller.ts | 8 ++-- .../shifts/dtos/shift-update.dto.ts | 2 +- .../shifts/services/shifts-get.service.ts | 8 ++-- .../shifts/services/shifts-upsert.service.ts | 30 +++++++------- .../time-tracker/shifts/shifts.module.ts | 7 ++-- .../controllers/timesheet.controller.ts | 4 +- .../services/timesheet-approval.service.ts | 4 +- .../timesheet-get-overview.service.ts | 6 +-- .../timesheets/timesheets.module.ts | 8 ++-- .../time-and-attendance.module.ts | 8 ++-- .../utils/constants.utils.ts | 2 +- src/time-and-attendance/utils/type.utils.ts | 26 +++++++++--- 21 files changed, 106 insertions(+), 112 deletions(-) rename src/time-and-attendance/modules/time-tracker/schedule-presets/services/{schedule-presets-query.service.ts => schedule-presets-get.service.ts} (92%) rename src/time-and-attendance/modules/time-tracker/schedule-presets/services/{schedule-presets-command.service.ts => schedule-presets-upsert.service.ts} (95%) diff --git a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts b/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts index 47dca6b..572972d 100644 --- a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts @@ -1,7 +1,8 @@ -import { Body, Controller, Delete, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; -import { CreateResult, ExpenseUpsertService, UpdateResult } from "../services/expense-upsert.service"; +import { Controller, Post, Param, ParseIntPipe, Body, Patch, Delete } from "@nestjs/common"; +import { CreateExpenseResult, UpdateExpenseResult } from "src/time-and-attendance/utils/type.utils"; +import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; import { updateExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-update.dto"; -import { ExpenseDto } from "../dtos/expense-create.dto"; +import { ExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-create.dto"; @Controller('expense') @@ -11,13 +12,13 @@ export class ExpenseController { @Post(':timesheet_id') create( @Param('timesheet_id', ParseIntPipe) timesheet_id: number, - @Body() dto: ExpenseDto): Promise{ + @Body() dto: ExpenseDto): Promise{ return this.upsert_service.createExpense(timesheet_id, dto); } @Patch() update( - @Body() body: { update :{ id: number; dto: updateExpenseDto }}): Promise{ + @Body() body: { update :{ id: number; dto: updateExpenseDto }}): Promise{ return this.upsert_service.updateExpense(body.update); } diff --git a/src/time-and-attendance/modules/expenses/dtos/expense-update.dto.ts b/src/time-and-attendance/modules/expenses/dtos/expense-update.dto.ts index 6108682..829f9c9 100644 --- a/src/time-and-attendance/modules/expenses/dtos/expense-update.dto.ts +++ b/src/time-and-attendance/modules/expenses/dtos/expense-update.dto.ts @@ -1,5 +1,5 @@ import { OmitType, PartialType } from "@nestjs/swagger"; -import { ExpenseDto } from "./expense-create.dto"; +import { ExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-create.dto"; export class updateExpenseDto extends PartialType ( OmitType(ExpenseDto, ['is_approved', 'timesheet_id'] as const) diff --git a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts index b2e8766..64390af 100644 --- a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts @@ -1,18 +1,12 @@ +import { CreateExpenseResult, UpdateExpensePayload, UpdateExpenseResult, DeleteExpenseResult, NormalizedExpense } from "src/time-and-attendance/utils/type.utils"; import { toDateFromString, toStringFromDate } from "src/time-and-attendance/utils/date-time.utils"; import { Injectable, NotFoundException } from "@nestjs/common"; -import { updateExpenseDto } from "../dtos/expense-update.dto"; -import { GetExpenseDto } from "../dtos/expense-get.dto"; +import { expense_select } from "src/time-and-attendance/utils/selects.utils"; import { PrismaService } from "src/prisma/prisma.service"; -import { ExpenseDto } from "../dtos/expense-create.dto"; -import { Prisma } from "@prisma/client"; +import { GetExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-get.dto"; +import { ExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-create.dto"; -type Normalized = { date: Date; comment: string; supervisor_comment?: string; }; - -export type CreateResult = { ok: true; data: GetExpenseDto } | { ok: false; error: any }; -export type UpdatePayload = { id: number; dto: updateExpenseDto }; -export type UpdateResult = { ok: true; id: number; data: GetExpenseDto } | { ok: false; id: number; error: any }; -export type DeleteResult = { ok: true; id: number; } | { ok: false; id: number; error: any }; @Injectable() export class ExpenseUpsertService { @@ -21,7 +15,7 @@ export class ExpenseUpsertService { //_________________________________________________________________ // CREATE //_________________________________________________________________ - async createExpense(timesheet_id: number, dto: ExpenseDto): Promise { + async createExpense(timesheet_id: number, dto: ExpenseDto): Promise { try { //normalize strings and dates const normed_expense = this.normalizeExpenseDto(dto); @@ -71,7 +65,7 @@ export class ExpenseUpsertService { //_________________________________________________________________ // UPDATE //_________________________________________________________________ - async updateExpense({id, dto}: UpdatePayload): Promise { + async updateExpense({id, dto}: UpdateExpensePayload): Promise { try { //checks for modifications const data: Record = {}; @@ -117,7 +111,7 @@ export class ExpenseUpsertService { //_________________________________________________________________ // DELETE //_________________________________________________________________ - async deleteExpense(expense_id: number): Promise { + async deleteExpense(expense_id: number): Promise { try { await this.prisma.$transaction(async (tx) => { const expense = await tx.expenses.findUnique({ @@ -139,7 +133,7 @@ export class ExpenseUpsertService { // LOCAL HELPERS //_________________________________________________________________ //makes sure that comments are the right length the date is of Date type - private normalizeExpenseDto(dto: ExpenseDto): Normalized { + private normalizeExpenseDto(dto: ExpenseDto): NormalizedExpense { const date = toDateFromString(dto.date); const comment = this.truncate280(dto.comment); const supervisor_comment = @@ -161,21 +155,4 @@ export class ExpenseUpsertService { if (Number.isNaN(parsed)) throw new Error(`Invalid value : ${value} for ${field}`); return parsed; }; - - -} - - - - export const expense_select = { - id: true, - timesheet_id: true, - bank_code_id: true, - attachment: true, - date: true, - amount: true, - mileage: true, - comment: true, - supervisor_comment: true, - is_approved: true, - } satisfies Prisma.ExpensesSelect; \ No newline at end of file +} \ No newline at end of file diff --git a/src/time-and-attendance/modules/expenses/services/expenses-archival.service.ts b/src/time-and-attendance/modules/expenses/services/expenses-archival.service.ts index fc17c63..0c354d6 100644 --- a/src/time-and-attendance/modules/expenses/services/expenses-archival.service.ts +++ b/src/time-and-attendance/modules/expenses/services/expenses-archival.service.ts @@ -1,6 +1,6 @@ -import { Injectable } from "@nestjs/common"; import { ExpensesArchive } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; +import { Injectable } from "@nestjs/common"; @Injectable() export class ExpensesArchivalService { diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index bd05224..a9d5c87 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -1,16 +1,16 @@ import { Controller, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch } from "@nestjs/common"; -import { SchedulePresetsCommandService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service"; +import { SchedulePresetsUpsertService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; import { SchedulePresetsApplyService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service"; -import { SchedulePresetsQueryService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service"; +import { SchedulePresetsGetService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service"; import { SchedulePresetsUpdateDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto"; import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; @Controller('schedule-presets') export class SchedulePresetsController { constructor( - private readonly commandService: SchedulePresetsCommandService, + private readonly upsertService: SchedulePresetsUpsertService, private readonly applyPresetsService: SchedulePresetsApplyService, - private readonly queryService: SchedulePresetsQueryService, + private readonly getService: SchedulePresetsGetService, ){} //used to create a schedule preset @@ -19,7 +19,7 @@ export class SchedulePresetsController { @Param('employee_id', ParseIntPipe) employee_id: number, @Body() dto: SchedulePresetsDto, ) { - return await this.commandService.createPreset(employee_id, dto); + return await this.upsertService.createPreset(employee_id, dto); } //used to update an already existing schedule preset @@ -28,7 +28,7 @@ export class SchedulePresetsController { @Param('preset_id', ParseIntPipe) preset_id: number, @Body() dto: SchedulePresetsUpdateDto, ) { - return await this.commandService.updatePreset(preset_id, dto); + return await this.upsertService.updatePreset(preset_id, dto); } //used to delete a schedule preset @@ -36,7 +36,7 @@ export class SchedulePresetsController { async deletePreset( @Param('preset_id') preset_id: number, ) { - return await this.commandService.deletePreset(preset_id); + return await this.upsertService.deletePreset(preset_id); } @@ -45,7 +45,7 @@ export class SchedulePresetsController { async findListById( @Param('employee_id', ParseIntPipe) employee_id: number, ) { - return this.queryService.findSchedulePresets(employee_id); + return this.getService.getSchedulePresets(employee_id); } diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts index cb1c512..86d7704 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts @@ -1,5 +1,5 @@ import { ArrayMinSize, IsArray, IsBoolean, IsOptional, IsString } from "class-validator"; -import { SchedulePresetShiftsDto } from "./create-schedule-preset-shifts.dto"; +import { SchedulePresetShiftsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto"; export class SchedulePresetsDto { @IsString() diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts index 1a8f9bf..ac6c595 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts @@ -1,6 +1,6 @@ -import { SchedulePresetsCommandService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service"; +import { SchedulePresetsUpsertService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; import { SchedulePresetsApplyService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service"; -import { SchedulePresetsQueryService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service"; +import { SchedulePresetsGetService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service"; import { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller"; import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; import { Module } from "@nestjs/common"; @@ -10,13 +10,13 @@ import { Module } from "@nestjs/common"; imports: [SharedModule], controllers: [SchedulePresetsController], providers: [ - SchedulePresetsCommandService, - SchedulePresetsQueryService, + SchedulePresetsUpsertService, + SchedulePresetsGetService, SchedulePresetsApplyService, ], exports:[ - SchedulePresetsCommandService, - SchedulePresetsQueryService, + SchedulePresetsUpsertService, + SchedulePresetsGetService, SchedulePresetsApplyService, ], }) export class SchedulePresetsModule {} \ No newline at end of file diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service.ts similarity index 92% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts rename to src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service.ts index b16911c..e73227d 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service.ts @@ -4,10 +4,10 @@ import { Injectable } from "@nestjs/common"; import { Prisma } from "@prisma/client"; @Injectable() -export class SchedulePresetsQueryService { +export class SchedulePresetsGetService { constructor( private readonly prisma: PrismaService ){} - async findSchedulePresets(employee_id: number): Promise { + async getSchedulePresets(employee_id: number): Promise { try { const presets = await this.prisma.schedulePresets.findMany({ where: { employee_id }, diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts similarity index 95% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts rename to src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts index cb35378..da45941 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service.ts +++ b/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts @@ -1,16 +1,13 @@ import { Injectable, BadRequestException, NotFoundException, ConflictException } from "@nestjs/common"; +import { CreatePresetResult, DeletePresetResult, UpdatePresetResult } from "src/time-and-attendance/utils/type.utils"; import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; import { Prisma, Weekday } from "@prisma/client"; -import { PrismaService } from "src/prisma/prisma.service"; import { toHHmmFromDate } from "src/time-and-attendance/utils/date-time.utils"; - -type DeleteResult = { ok: true; id: number; } | { ok: false; id: number; error: any }; -type CreateResult = { ok: true; } | { ok: false; error: any }; -type UpdateResult = { ok: true; id: number; data: SchedulePresetsDto } | { ok: false; id: number; error: any }; +import { PrismaService } from "src/prisma/prisma.service"; @Injectable() -export class SchedulePresetsCommandService { +export class SchedulePresetsUpsertService { constructor( private readonly prisma: PrismaService, private readonly typeResolver : BankCodesResolver, @@ -18,7 +15,7 @@ export class SchedulePresetsCommandService { //_________________________________________________________________ // CREATE //_________________________________________________________________ - async createPreset( employee_id: number, dto: SchedulePresetsDto): Promise { + async createPreset( employee_id: number, dto: SchedulePresetsDto): Promise { try { const shifts_data = await this.resolveAndBuildPresetShifts(dto); if(!shifts_data) throw new BadRequestException(`Employee with id: ${employee_id} or dto not found`); @@ -49,7 +46,7 @@ export class SchedulePresetsCommandService { //_________________________________________________________________ // UPDATE //_________________________________________________________________ - async updatePreset( preset_id: number, dto: SchedulePresetsDto ): Promise { + async updatePreset( preset_id: number, dto: SchedulePresetsDto ): Promise { try { const existing = await this.prisma.schedulePresets.findFirst({ where: { id: preset_id }, @@ -137,7 +134,7 @@ export class SchedulePresetsCommandService { //_________________________________________________________________ // DELETE //_________________________________________________________________ - async deletePreset( preset_id: number ): Promise { + async deletePreset( preset_id: number ): Promise { try { await this.prisma.$transaction(async (tx) => { const preset = await tx.schedulePresets.findFirst({ diff --git a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts index 6c4968c..6928cc8 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts @@ -1,8 +1,8 @@ import { BadRequestException, Body, Controller, Delete, Param, Patch, Post } from "@nestjs/common"; -import { CreateResult, UpdateResult } from "src/time-and-attendance/utils/type.utils"; +import { CreateShiftResult, UpdateShiftResult } from "src/time-and-attendance/utils/type.utils"; import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; -import { ShiftDto } from "../dtos/shift-create.dto"; +import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; @Controller('shift') @@ -11,7 +11,7 @@ export class ShiftController { @Post('create') createBatch( - @Body()dtos: ShiftDto[]): Promise { + @Body()dtos: ShiftDto[]): Promise { const list = Array.isArray(dtos) ? dtos : []; if(list.length === 0) throw new BadRequestException('Body is missing or invalid (create shifts)'); return this.upsert_service.createShifts(dtos) @@ -21,7 +21,7 @@ export class ShiftController { //change Body to receive dtos @Patch('update') updateBatch( - @Body() dtos: UpdateShiftDto[]): Promise{ + @Body() dtos: UpdateShiftDto[]): Promise{ const list = Array.isArray(dtos) ? dtos: []; if(list.length === 0) throw new BadRequestException('Body is missing or invalid (update shifts)'); return this.upsert_service.updateShifts(dtos); diff --git a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts b/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts index 99645cb..ce16f8e 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts @@ -1,5 +1,5 @@ import { PartialType, OmitType } from "@nestjs/swagger"; -import { ShiftDto } from "./shift-create.dto"; +import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; import { IsInt } from "class-validator"; export class UpdateShiftDto extends PartialType( diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts index 8971cf8..c1c2e12 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts @@ -1,10 +1,11 @@ -import { toStringFromDate, toStringFromHHmm } from "../../../../utils/date-time.utils"; +import { toStringFromDate, toStringFromHHmm } from "src/time-and-attendance/utils/date-time.utils"; import { Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { shift_select } from "src/time-and-attendance/utils/selects.utils"; -import { GetShiftDto } from "../dtos/shift-get.dto"; +import { GetShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto"; -/** + +/** * _____________________________________________________________________________________ * * @@ -15,6 +16,7 @@ import { GetShiftDto } from "../dtos/shift-get.dto"; * _____________________________________________________________________________________ */ + @Injectable() export class ShiftsGetService { constructor( diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts index cca5e5a..e16797c 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts @@ -1,12 +1,12 @@ -import { CreateResult, NormedOk, NormedErr, UpdatePayload, UpdateResult, Normalized, UpdateChanges } from "src/time-and-attendance/utils/type.utils"; -import { overlaps, toDateFromString, toHHmmFromString, toStringFromDate, toStringFromHHmm } from "../../../../utils/date-time.utils"; -import { BadRequestException, ConflictException, Injectable, NotFoundException } from "@nestjs/common"; +import { CreateShiftResult, NormedOk, NormedErr, UpdateShiftResult, UpdateShiftPayload, UpdateShiftChanges, Normalized } from "src/time-and-attendance/utils/type.utils"; +import { overlaps, toStringFromHHmm, toStringFromDate, toDateFromString, toHHmmFromString } from "src/time-and-attendance/utils/date-time.utils"; +import { Injectable, BadRequestException, ConflictException, NotFoundException } from "@nestjs/common"; import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; import { PrismaService } from "src/prisma/prisma.service"; import { shift_select } from "src/time-and-attendance/utils/selects.utils"; -import { GetShiftDto } from "../dtos/shift-get.dto"; -import { ShiftDto } from "../dtos/shift-create.dto"; +import { GetShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto"; +import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; @@ -25,7 +25,7 @@ export class ShiftsUpsertService { //checks for overlaping shifts //create new shifts //calculate overtime - async createShifts(dtos: ShiftDto[]): Promise { + async createShifts(dtos: ShiftDto[]): Promise { if (!Array.isArray(dtos) || dtos.length === 0) return []; const normed_shift: Array = dtos.map((dto, index) => { @@ -66,14 +66,14 @@ export class ShiftsUpsertService { }); return dtos.map((_dto, key) => indices.includes(key) - ? ({ ok: false, error: err } as CreateResult) + ? ({ ok: false, error: err } as CreateShiftResult) : ({ ok: false, error: new BadRequestException('Batch aborted due to overlaps in another date group') }) ); } } } return this.prisma.$transaction(async (tx) => { - const results: CreateResult[] = Array.from({ length: dtos.length }, () => ({ ok: false, error: new Error('uninitialized') })); + const results: CreateShiftResult[] = Array.from({ length: dtos.length }, () => ({ ok: false, error: new Error('uninitialized') })); normed_shift.forEach((x, i) => { @@ -157,16 +157,16 @@ export class ShiftsUpsertService { // update shifts in DB // recalculate overtime after update // return an updated version to display - async updateShifts(dtos: UpdateShiftDto[]): Promise { + async updateShifts(dtos: UpdateShiftDto[]): Promise { if (!Array.isArray(dtos) || dtos.length === 0) return []; - const updates: UpdatePayload[] = dtos.map((item) => { + const updates: UpdateShiftPayload[] = dtos.map((item) => { const { id, ...rest } = item; if (!Number.isInteger(id)) { throw new BadRequestException('Update shift payload is missing a valid id'); } - const changes: UpdateChanges = {}; + const changes: UpdateShiftChanges = {}; if (rest.date !== undefined) changes.date = rest.date; if (rest.start_time !== undefined) changes.start_time = rest.start_time; if (rest.end_time !== undefined) changes.end_time = rest.end_time; @@ -189,12 +189,12 @@ export class ShiftsUpsertService { const existing = regroup_id.get(update.id); if (!existing) { return updates.map(exist => exist.id === update.id - ? ({ ok: false, id: update.id, error: new NotFoundException(`Shift with id: ${update.id} not found`) } as UpdateResult) + ? ({ ok: false, id: update.id, error: new NotFoundException(`Shift with id: ${update.id} not found`) } as UpdateShiftResult) : ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to missing shift') })); } if (existing.is_approved) { return updates.map(exist => exist.id === update.id - ? ({ ok: false, id: update.id, error: new BadRequestException('Approved shift cannot be updated') } as UpdateResult) + ? ({ ok: false, id: update.id, error: new BadRequestException('Approved shift cannot be updated') } as UpdateShiftResult) : ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to approved shift in update set') })); } } @@ -249,7 +249,7 @@ export class ShiftsUpsertService { message: 'New shift overlaps with existing shift(s)', conflicts: [{ start_time: toStringFromHHmm(conflict.start), end_time: toStringFromHHmm(conflict.end), type: 'UNKNOWN' }], }) - } as UpdateResult) + } as UpdateShiftResult) : ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to overlap in another update') }) ); } @@ -271,7 +271,7 @@ export class ShiftsUpsertService { } } - const results: UpdateResult[] = []; + const results: UpdateShiftResult[] = []; for (const planned of planned_updates) { const data: any = {}; const { dto } = planned.update; diff --git a/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts b/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts index 2669e7d..e031f31 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts @@ -1,7 +1,8 @@ + import { BusinessLogicsModule } from 'src/time-and-attendance/domains/business-logics.module'; -import { ShiftsUpsertService } from './services/shifts-upsert.service'; -import { ShiftsGetService } from './services/shifts-get.service'; -import { ShiftController } from './controllers/shift.controller'; +import { ShiftsUpsertService } from 'src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service'; +import { ShiftsGetService } from 'src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service'; +import { ShiftController } from 'src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller'; import { Module } from '@nestjs/common'; @Module({ diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts index 7eb7209..9ec6d6e 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts @@ -1,6 +1,6 @@ -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; -import { GetTimesheetsOverviewService } from "../services/timesheet-get-overview.service"; +import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service"; import { BadRequestException, Controller, Get, Query} from "@nestjs/common"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; @Controller('timesheets') export class TimesheetController { diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-approval.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-approval.service.ts index c22d0dc..088b5b7 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-approval.service.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-approval.service.ts @@ -1,7 +1,7 @@ -import { Injectable } from "@nestjs/common"; -import { Prisma, Timesheets } from "@prisma/client"; import { BaseApprovalService } from "src/common/shared/base-approval.service"; +import { Prisma, Timesheets } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; +import { Injectable } from "@nestjs/common"; @Injectable() export class TimesheetApprovalService extends BaseApprovalService{ diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts index 24aa158..e89d684 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -1,8 +1,8 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; -import { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/time-and-attendance/utils/constants.utils"; import { sevenDaysFrom, toStringFromDate, toHHmmFromDate, toDateFromString } from "src/time-and-attendance/utils/date-time.utils"; +import { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/time-and-attendance/utils/constants.utils"; +import { Injectable, NotFoundException } from "@nestjs/common"; import { TotalExpenses, TotalHours } from "src/time-and-attendance/utils/type.utils"; +import { PrismaService } from "src/prisma/prisma.service"; @Injectable() export class GetTimesheetsOverviewService { diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts b/src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts index d9375cf..595d660 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts @@ -1,7 +1,7 @@ -import { GetTimesheetsOverviewService } from './services/timesheet-get-overview.service'; -import { TimesheetArchiveService } from './services/timesheet-archive.service'; -import { TimesheetController } from './controllers/timesheet.controller'; -import { SharedModule } from '../../shared/shared.module'; +import { GetTimesheetsOverviewService } from 'src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service'; +import { TimesheetArchiveService } from 'src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service'; +import { TimesheetController } from 'src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller'; +import { SharedModule } from 'src/time-and-attendance/modules/shared/shared.module'; import { Module } from '@nestjs/common'; @Module({ diff --git a/src/time-and-attendance/time-and-attendance.module.ts b/src/time-and-attendance/time-and-attendance.module.ts index 31155e9..ff54d41 100644 --- a/src/time-and-attendance/time-and-attendance.module.ts +++ b/src/time-and-attendance/time-and-attendance.module.ts @@ -1,6 +1,6 @@ -import { SchedulePresetsCommandService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-command.service"; +import { SchedulePresetsUpsertService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service"; -import { SchedulePresetsQueryService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-query.service"; +import { SchedulePresetsGetService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service"; import { SchedulePresetsApplyService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service"; import { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller"; import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; @@ -31,8 +31,8 @@ import { Module } from "@nestjs/common"; ShiftsGetService, ShiftsUpsertService, ExpenseUpsertService, - SchedulePresetsCommandService, - SchedulePresetsQueryService, + SchedulePresetsUpsertService, + SchedulePresetsGetService, SchedulePresetsApplyService, ], exports: [], diff --git a/src/time-and-attendance/utils/constants.utils.ts b/src/time-and-attendance/utils/constants.utils.ts index c02df87..e2f20fc 100644 --- a/src/time-and-attendance/utils/constants.utils.ts +++ b/src/time-and-attendance/utils/constants.utils.ts @@ -2,7 +2,7 @@ export const NUMBER_OF_TIMESHEETS_TO_RETURN = 2; export const DAILY_LIMIT_HOURS = 8; export const WEEKLY_LIMIT_HOURS = 40; export const PAY_PERIOD_ANCHOR = 2023-12-17; -export const ANCHOR_ISO = '2023-12-17'; // ancre date +export const ANCHOR_ISO = '2023-12-17'; export const PERIOD_DAYS = 14; export const PERIODS_PER_YEAR = 26; export const MS_PER_DAY = 86_400_000; diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts index 0903b87..0264f3f 100644 --- a/src/time-and-attendance/utils/type.utils.ts +++ b/src/time-and-attendance/utils/type.utils.ts @@ -4,6 +4,9 @@ import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shi import { GetShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto"; import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; import { Prisma } from "@prisma/client"; +import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; +import { GetExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-get.dto"; +import { updateExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-update.dto"; export type TotalHours = { regular: number; @@ -29,11 +32,24 @@ export type ShiftWithOvertimeDto = { overtime: WeekOvertimeSummary; }; -export type CreateResult = { ok: true; data: ShiftWithOvertimeDto } | { ok: false; error: any }; -export type UpdateChanges = Omit; -export type UpdatePayload = { id: number; dto: UpdateChanges }; -export type UpdateResult = { ok: true; id: number; data: ShiftWithOvertimeDto } | { ok: false; id: number; error: any }; -export type DeleteResult = { ok: true; id: number; overtime: WeekOvertimeSummary } | { ok: false; id: number; error: any }; +export type CreateShiftResult = { ok: true; data: ShiftWithOvertimeDto } | { ok: false; error: any }; +export type UpdateShiftChanges = Omit; +export type UpdateShiftPayload = { id: number; dto: UpdateShiftChanges }; +export type UpdateShiftResult = { ok: true; id: number; data: ShiftWithOvertimeDto } | { ok: false; id: number; error: any }; +export type DeleteShiftResult = { ok: true; id: number; overtime: WeekOvertimeSummary } | { ok: false; id: number; error: any }; + +export type DeletePresetResult = { ok: true; id: number; } | { ok: false; id: number; error: any }; +export type CreatePresetResult = { ok: true; } | { ok: false; error: any }; +export type UpdatePresetResult = { ok: true; id: number; data: SchedulePresetsDto } | { ok: false; id: number; error: any }; + +export type NormalizedExpense = { date: Date; comment: string; supervisor_comment?: string; }; + +export type CreateExpenseResult = { ok: true; data: GetExpenseDto } | { ok: false; error: any }; +export type UpdateExpensePayload = { id: number; dto: updateExpenseDto }; +export type UpdateExpenseResult = { ok: true; id: number; data: GetExpenseDto } | { ok: false; id: number; error: any }; +export type DeleteExpenseResult = { ok: true; id: number; } | { ok: false; id: number; error: any }; + + export type NormedOk = { index: number; dto: ShiftDto; normed: Normalized }; export type NormedErr = { index: number; error: any }; From 78aec894ed28118cf288d1bc1f70b8e62772922e Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 30 Oct 2025 14:23:52 -0400 Subject: [PATCH 22/57] fix(auth): change auth to work with remote docker lab --- Dockerfile | 2 +- .../controllers/auth.controller.ts | 2 +- src/main.ts | 110 +++++++++--------- 3 files changed, 57 insertions(+), 57 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7063f1a..080b450 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,7 @@ ENV AUTHENTIK_AUTH_URL="https://auth.targo.ca/application/o/authorize/" ENV AUTHENTIK_TOKEN_URL="https://auth.targo.ca/application/o/token/" ENV AUTHENTIK_USERINFO_URL="https://auth.targo.ca/application/o/userinfo/" -ENV TARGO_FRONTEND_URI="http://localhost:9000/" +ENV TARGO_FRONTEND_URI="http://10.100.251.2/" ENV ATTACHMENTS_SERVER_ID="server" ENV ATTACHMENTS_ROOT=C:/ diff --git a/src/identity-and-account/authentication/controllers/auth.controller.ts b/src/identity-and-account/authentication/controllers/auth.controller.ts index 15255f1..0ec1ff2 100644 --- a/src/identity-and-account/authentication/controllers/auth.controller.ts +++ b/src/identity-and-account/authentication/controllers/auth.controller.ts @@ -12,7 +12,7 @@ export class AuthController { @Get('/callback') @UseGuards(OIDCLoginGuard) loginCallback(@Req() req: Request, @Res() res: Response) { - res.redirect('http://localhost:9000/#/login-success'); + res.redirect('http://10.100.251.2:9011/#/login-success'); } @Get('/me') diff --git a/src/main.ts b/src/main.ts index 88237b6..fb35236 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,10 +1,10 @@ import 'reflect-metadata'; //import and if case for @nestjs/schedule Cron jobs import * as nodeCrypto from 'crypto'; -if(!(globalThis as any).crypto) { - (globalThis as any).crypto = nodeCrypto; +if (!(globalThis as any).crypto) { + (globalThis as any).crypto = nodeCrypto; } -import { ensureAttachmentsTmpDir } from './config/attachment.fs'; +import { ensureAttachmentsTmpDir } from './config/attachment.fs'; import { resolveAttachmentsRoot } from './config/attachment.config';// log to be removed post dev import { ATT_TMP_DIR } from './config/attachment.config'; // log to be removed post dev @@ -20,72 +20,72 @@ import * as session from 'express-session'; import * as passport from 'passport'; async function bootstrap() { - const app = await NestFactory.create(AppModule); + const app = await NestFactory.create(AppModule); - const reflector = app.get(Reflector); //setup Reflector for Roles() + const reflector = app.get(Reflector); //setup Reflector for Roles() - app.useGlobalGuards( - // new JwtAuthGuard(reflector), //Authentification JWT - new RolesGuard(reflector), //deny-by-default and Role-based Access Control - new OwnershipGuard(reflector, app.get(ModuleRef)), //Global use of OwnershipGuard, not implemented yet - ); + app.useGlobalGuards( + // new JwtAuthGuard(reflector), //Authentification JWT + new RolesGuard(reflector), //deny-by-default and Role-based Access Control + new OwnershipGuard(reflector, app.get(ModuleRef)), //Global use of OwnershipGuard, not implemented yet + ); - // Authentication and session - app.use(session({ - secret: 'This is a super secret dev secret that you cant share with anyone', - resave: false, - saveUninitialized: false, - rolling: true, - cookie: { - maxAge: 30 * 60 * 1000, - httpOnly: true, - } - })) - app.use(passport.initialize()); - app.use(passport.session()); + // Authentication and session + app.use(session({ + secret: 'This is a super secret dev secret that you cant share with anyone', + resave: false, + saveUninitialized: false, + rolling: true, + cookie: { + maxAge: 30 * 60 * 1000, + httpOnly: true, + } + })) + app.use(passport.initialize()); + app.use(passport.session()); - // Enable CORS - app.enableCors({ - origin: 'http://localhost:9000', - credentials: true, - }); + // Enable CORS + app.enableCors({ + origin: ['http://10.100.251.2:9011', 'http://10.100.251.2:9012', 'http://10.100.251.2:9013'], + credentials: true, + }); + + //swagger config + const config = new DocumentBuilder() + .setTitle('Targo_Backend') + .setDescription('Documentation de l`API REST pour Targo (NestJS + Prisma)') + .setVersion('1.0') + .addBearerAuth({ + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + name: 'Authorization', + description: 'Invalid JWT token', + in: 'header', + }, 'access-token') + .addTag('Users') + .addTag('Employees') + .addTag('Customers') + .addTag('Timesheets') + .addTag('Shifts') + .addTag('Leave Requests') + .addTag('Shift Codes') + .addTag('OAuth Access Tokens') + .addTag('Authorization') + .build(); - //swagger config - const config = new DocumentBuilder() - .setTitle('Targo_Backend') - .setDescription('Documentation de l`API REST pour Targo (NestJS + Prisma)') - .setVersion('1.0') - .addBearerAuth({ - type: 'http', - scheme: 'bearer', - bearerFormat: 'JWT', - name: 'Authorization', - description: 'Invalid JWT token', - in: 'header', - }, 'access-token') - .addTag('Users') - .addTag('Employees') - .addTag('Customers') - .addTag('Timesheets') - .addTag('Shifts') - .addTag('Leave Requests') - .addTag('Shift Codes') - .addTag('OAuth Access Tokens') - .addTag('Authorization') - .build(); - //document builder for swagger docs const documentFactory = () => SwaggerModule.createDocument(app, config); const document = documentFactory() SwaggerModule.setup('api/docs', app, document); writeFileSync('./docs/swagger/swagger-spec.json', JSON.stringify(document, null, 2)); - + // logs to be removed post dev console.log('[ENV.ATTACHMENTS_ROOT]', process.env.ATTACHMENTS_ROOT); console.log('[resolveAttachmentsRoot()]', resolveAttachmentsRoot()); console.log('[ATT_TMP_DIR()]', ATT_TMP_DIR()); - await ensureAttachmentsTmpDir(); - await app.listen(process.env.PORT ?? 3000); + await ensureAttachmentsTmpDir(); + await app.listen(process.env.PORT ?? 3000); } bootstrap(); From c0fbad006d7c979b75c43ee46a8717d6acb4defa Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 31 Oct 2025 08:35:24 -0400 Subject: [PATCH 23/57] fix(attachments): fix typo --- src/modules/attachments/controllers/attachments.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/attachments/controllers/attachments.controller.ts b/src/modules/attachments/controllers/attachments.controller.ts index 4999771..963bb5c 100644 --- a/src/modules/attachments/controllers/attachments.controller.ts +++ b/src/modules/attachments/controllers/attachments.controller.ts @@ -74,7 +74,7 @@ export class AttachmentsController { return this.prisma.attachmentVariants.findMany({ where: { attachment_id: num_id }, orderBy: { variant: 'asc'}, - select: { variant: true, bytes: true, width: true, height: true, patch: true, created_at: true }, + select: { variant: true, bytes: true, width: true, height: true, path: true, created_at: true }, }); } From 9f0da467ae0ba394f927e1b77237ba491a1faf9a Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 31 Oct 2025 08:50:13 -0400 Subject: [PATCH 24/57] fix(timesheet): removed validation/transformation from ensureTimesheet and use the param Date format inside the function --- .../services/timesheet-get-overview.service.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts index e89d684..81bc330 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -178,11 +178,10 @@ export class GetTimesheetsOverviewService { }; } - private ensureTimesheet = async (employee_id: number, start_date: Date | string) => { - const start = toDateFromString(start_date); + private ensureTimesheet = async (employee_id: number, start_date: Date) => { let row = await this.prisma.timesheets.findFirst({ - where: { employee_id, start_date: start }, + where: { employee_id, start_date: start_date }, include: { employee: { include: { user: true } }, shift: { include: { bank_code: true } }, @@ -194,13 +193,13 @@ export class GetTimesheetsOverviewService { await this.prisma.timesheets.create({ data: { employee_id, - start_date: start, + start_date: start_date, is_approved: false }, }); row = await this.prisma.timesheets.findFirst({ - where: { employee_id, start_date: start }, + where: { employee_id, start_date: start_date }, include: { employee: { include: { user: true } }, shift: { include: { bank_code: true } }, From e0cefc8ec92e111946170a38910de6a928b395af Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 31 Oct 2025 12:11:55 -0400 Subject: [PATCH 25/57] refactor(expenses): modified createExpense signature. removed timesheet_id from the param. ajusted auth logic --- docs/swagger/swagger-spec.json | 17 ++++------------- .../controllers/auth.controller.ts | 3 ++- src/main.ts | 2 +- .../expenses/controllers/expense.controller.ts | 12 +++++------- .../expenses/services/expense-upsert.service.ts | 11 ++++++++--- .../services/timesheet-get-overview.service.ts | 9 +++++---- .../utils/date-time.utils.ts | 2 +- 7 files changed, 26 insertions(+), 30 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index e9c9b27..ae43cd0 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -605,19 +605,10 @@ ] } }, - "/expense/{timesheet_id}": { + "/expense/create": { "post": { "operationId": "ExpenseController_create", - "parameters": [ - { - "name": "timesheet_id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], + "parameters": [], "requestBody": { "required": true, "content": { @@ -638,7 +629,7 @@ ] } }, - "/expense": { + "/expense/update": { "patch": { "operationId": "ExpenseController_update", "parameters": [], @@ -652,7 +643,7 @@ ] } }, - "/expense/{expense_id}": { + "/expense/delete/{expense_id}": { "delete": { "operationId": "ExpenseController_remove", "parameters": [ diff --git a/src/identity-and-account/authentication/controllers/auth.controller.ts b/src/identity-and-account/authentication/controllers/auth.controller.ts index 0ec1ff2..43c9397 100644 --- a/src/identity-and-account/authentication/controllers/auth.controller.ts +++ b/src/identity-and-account/authentication/controllers/auth.controller.ts @@ -12,7 +12,8 @@ export class AuthController { @Get('/callback') @UseGuards(OIDCLoginGuard) loginCallback(@Req() req: Request, @Res() res: Response) { - res.redirect('http://10.100.251.2:9011/#/login-success'); + // res.redirect('http://10.100.251.2:9011/#/login-success'); + res.redirect('http://localhost:9000/#/login-success'); } @Get('/me') diff --git a/src/main.ts b/src/main.ts index fb35236..7c06222 100644 --- a/src/main.ts +++ b/src/main.ts @@ -46,7 +46,7 @@ async function bootstrap() { // Enable CORS app.enableCors({ - origin: ['http://10.100.251.2:9011', 'http://10.100.251.2:9012', 'http://10.100.251.2:9013'], + origin: ['http://10.100.251.2:9011', 'http://10.100.251.2:9012', 'http://10.100.251.2:9013', 'http://localhost:9000'], credentials: true, }); diff --git a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts b/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts index 572972d..6e4f495 100644 --- a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts @@ -9,20 +9,18 @@ import { ExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expens export class ExpenseController { constructor( private readonly upsert_service: ExpenseUpsertService ){} - @Post(':timesheet_id') - create( - @Param('timesheet_id', ParseIntPipe) timesheet_id: number, - @Body() dto: ExpenseDto): Promise{ - return this.upsert_service.createExpense(timesheet_id, dto); + @Post('create') + create(@Body() dto: ExpenseDto): Promise{ + return this.upsert_service.createExpense(dto); } - @Patch() + @Patch('update') update( @Body() body: { update :{ id: number; dto: updateExpenseDto }}): Promise{ return this.upsert_service.updateExpense(body.update); } - @Delete(':expense_id') + @Delete('delete/:expense_id') remove(@Param('expense_id') expense_id: number) { return this.upsert_service.deleteExpense(expense_id); } diff --git a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts index 64390af..38990a0 100644 --- a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts @@ -15,7 +15,7 @@ export class ExpenseUpsertService { //_________________________________________________________________ // CREATE //_________________________________________________________________ - async createExpense(timesheet_id: number, dto: ExpenseDto): Promise { + async createExpense( dto: ExpenseDto): Promise { try { //normalize strings and dates const normed_expense = this.normalizeExpenseDto(dto); @@ -24,11 +24,16 @@ export class ExpenseUpsertService { const parsed_amount = this.parseOptionalNumber(dto.amount, "amount"); const parsed_mileage = this.parseOptionalNumber(dto.mileage, "mileage"); const parsed_attachment = this.parseOptionalNumber(dto.attachment, "attachment"); - + + const timesheet = await this.prisma.timesheets.findUnique({ + where: { id: dto.timesheet_id }, + select: { id: true }, + }); + if(!timesheet) throw new NotFoundException(`Timesheet with id ${dto.timesheet_id} not found`); //create a new expense const expense = await this.prisma.expenses.create({ data: { - timesheet_id, + timesheet_id: timesheet.id, bank_code_id: dto.bank_code_id, attachment: parsed_attachment, date: normed_expense.date, diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts index 81bc330..e89d684 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -178,10 +178,11 @@ export class GetTimesheetsOverviewService { }; } - private ensureTimesheet = async (employee_id: number, start_date: Date) => { + 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_date }, + where: { employee_id, start_date: start }, include: { employee: { include: { user: true } }, shift: { include: { bank_code: true } }, @@ -193,13 +194,13 @@ export class GetTimesheetsOverviewService { await this.prisma.timesheets.create({ data: { employee_id, - start_date: start_date, + start_date: start, is_approved: false }, }); row = await this.prisma.timesheets.findFirst({ - where: { employee_id, start_date: start_date }, + where: { employee_id, start_date: start }, include: { employee: { include: { user: true } }, shift: { include: { bank_code: true } }, diff --git a/src/time-and-attendance/utils/date-time.utils.ts b/src/time-and-attendance/utils/date-time.utils.ts index c43337d..07ea016 100644 --- a/src/time-and-attendance/utils/date-time.utils.ts +++ b/src/time-and-attendance/utils/date-time.utils.ts @@ -40,7 +40,7 @@ export const toHHmmFromDate = (input: Date | string): string => { //converts Date format to string export const toDateFromString = (ymd: string | Date): Date => { - return new Date(`${ymd}T00:00:00:000Z`); + return new Date(`${ymd}`); } export const toUTCDateFromString = (iso: string | Date) => { From e5484da39a22c5dfe015d5306f1373c2524a166e Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 31 Oct 2025 12:34:12 -0400 Subject: [PATCH 26/57] refactor(expenses): added email to req inside controller and pass email to the function to pin point the right timesheet --- .../controllers/expense.controller.ts | 8 ++++--- .../services/expense-upsert.service.ts | 22 +++++++++++++------ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts b/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts index 6e4f495..594d583 100644 --- a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Post, Param, ParseIntPipe, Body, Patch, Delete } from "@nestjs/common"; +import { Controller, Post, Param, Body, Patch, Delete, Req, UnauthorizedException } from "@nestjs/common"; import { CreateExpenseResult, UpdateExpenseResult } from "src/time-and-attendance/utils/type.utils"; import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; import { updateExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-update.dto"; @@ -10,8 +10,10 @@ export class ExpenseController { constructor( private readonly upsert_service: ExpenseUpsertService ){} @Post('create') - create(@Body() dto: ExpenseDto): Promise{ - return this.upsert_service.createExpense(dto); + create( @Req() req, @Body() dto: ExpenseDto): Promise{ + const email = req.user?.email; + if(!email) throw new UnauthorizedException('Unauthorized User'); + return this.upsert_service.createExpense(dto, email); } @Patch('update') diff --git a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts index 38990a0..9687bca 100644 --- a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts @@ -1,22 +1,29 @@ import { CreateExpenseResult, UpdateExpensePayload, UpdateExpenseResult, DeleteExpenseResult, NormalizedExpense } from "src/time-and-attendance/utils/type.utils"; import { toDateFromString, toStringFromDate } from "src/time-and-attendance/utils/date-time.utils"; -import { Injectable, NotFoundException } from "@nestjs/common"; +import { Injectable, NotFoundException, Req } from "@nestjs/common"; import { expense_select } from "src/time-and-attendance/utils/selects.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { GetExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-get.dto"; import { ExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-create.dto"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; @Injectable() export class ExpenseUpsertService { - constructor(private readonly prisma: PrismaService) { } + constructor( + private readonly prisma: PrismaService, + private readonly emailResolver: EmailToIdResolver, + ) { } //_________________________________________________________________ // CREATE //_________________________________________________________________ - async createExpense( dto: ExpenseDto): Promise { + async createExpense( dto: ExpenseDto, email: string): Promise { try { + //fetch employee_id using req.user.email + const employee_id = await this.emailResolver.findIdByEmail(email); + //normalize strings and dates const normed_expense = this.normalizeExpenseDto(dto); @@ -24,12 +31,13 @@ export class ExpenseUpsertService { const parsed_amount = this.parseOptionalNumber(dto.amount, "amount"); const parsed_mileage = this.parseOptionalNumber(dto.mileage, "mileage"); const parsed_attachment = this.parseOptionalNumber(dto.attachment, "attachment"); - - const timesheet = await this.prisma.timesheets.findUnique({ - where: { id: dto.timesheet_id }, - select: { id: true }, + + const timesheet = await this.prisma.timesheets.findFirst({ + where: { id: dto.timesheet_id, employee_id: employee_id }, + select: { id: true, employee_id: true }, }); if(!timesheet) throw new NotFoundException(`Timesheet with id ${dto.timesheet_id} not found`); + //create a new expense const expense = await this.prisma.expenses.create({ data: { From 6c746aa3c20e1ab553888c713111c080338df43a Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 31 Oct 2025 12:37:11 -0400 Subject: [PATCH 27/57] refactor(expenses): added start_date to find the right timesheet using expense.date --- .../modules/expenses/services/expense-upsert.service.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts index 9687bca..d2c4cb7 100644 --- a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts @@ -1,6 +1,6 @@ import { CreateExpenseResult, UpdateExpensePayload, UpdateExpenseResult, DeleteExpenseResult, NormalizedExpense } from "src/time-and-attendance/utils/type.utils"; -import { toDateFromString, toStringFromDate } from "src/time-and-attendance/utils/date-time.utils"; +import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils"; import { Injectable, NotFoundException, Req } from "@nestjs/common"; import { expense_select } from "src/time-and-attendance/utils/selects.utils"; import { PrismaService } from "src/prisma/prisma.service"; @@ -27,13 +27,16 @@ export class ExpenseUpsertService { //normalize strings and dates const normed_expense = this.normalizeExpenseDto(dto); + //finds the timesheet using expense.date + const start_date = weekStartSunday(normed_expense.date); + //parse numbers const parsed_amount = this.parseOptionalNumber(dto.amount, "amount"); const parsed_mileage = this.parseOptionalNumber(dto.mileage, "mileage"); const parsed_attachment = this.parseOptionalNumber(dto.attachment, "attachment"); const timesheet = await this.prisma.timesheets.findFirst({ - where: { id: dto.timesheet_id, employee_id: employee_id }, + where: { start_date, employee_id }, select: { id: true, employee_id: true }, }); if(!timesheet) throw new NotFoundException(`Timesheet with id ${dto.timesheet_id} not found`); From bb60887a0d82c4ef31142643b1ad8bfa2b717649 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 31 Oct 2025 14:04:07 -0400 Subject: [PATCH 28/57] refactor(timesheet): used session data and removed email from query of the Get function --- docs/swagger/swagger-spec.json | 12 ++-------- .../controllers/timesheet.controller.ts | 24 +++++++------------ .../timesheet-get-overview.service.ts | 10 ++++++-- 3 files changed, 18 insertions(+), 28 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index ae43cd0..af88b3e 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -342,20 +342,12 @@ "get": { "operationId": "TimesheetController_getTimesheetByIds", "parameters": [ - { - "name": "employee_email", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - }, { "name": "year", "required": true, "in": "query", "schema": { - "type": "string" + "type": "number" } }, { @@ -363,7 +355,7 @@ "required": true, "in": "query", "schema": { - "type": "string" + "type": "number" } } ], diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts index 9ec6d6e..414930a 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts @@ -1,26 +1,18 @@ +import { Controller, Get, ParseIntPipe, Query, Req, UnauthorizedException} from "@nestjs/common"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service"; -import { BadRequestException, Controller, Get, Query} from "@nestjs/common"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; @Controller('timesheets') export class TimesheetController { - constructor( - private readonly timesheetOverview: GetTimesheetsOverviewService, - private readonly emailResolver: EmailToIdResolver, - ){} + constructor( private readonly timesheetOverview: GetTimesheetsOverviewService ){} @Get() async getTimesheetByIds( - @Query('employee_email') employee_email: string, - @Query('year') year: string, - @Query('period_number') period_number: string, + @Req() req, + @Query('year', ParseIntPipe) year: number, + @Query('period_number', ParseIntPipe) period_number: number, ) { - if (!employee_email || !year || !period_number) { - throw new BadRequestException('Query params "employee_email", "year" and eriod_number" are required.'); - } - const employee_id = await this.emailResolver.findIdByEmail(employee_email); - const pay_year = Number(year); - const period_num = Number(period_number); - return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(employee_id, pay_year, period_num); + const email = req.user?.email; + if(!email) throw new UnauthorizedException('Unauthorized User'); + return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(email, year, period_number); } } diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts index e89d684..c6da872 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -3,19 +3,25 @@ import { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/time-and-attendance/utils/co import { Injectable, NotFoundException } from "@nestjs/common"; import { TotalExpenses, TotalHours } from "src/time-and-attendance/utils/type.utils"; import { PrismaService } from "src/prisma/prisma.service"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; @Injectable() export class GetTimesheetsOverviewService { - constructor(private readonly prisma: PrismaService) { } + constructor( + private readonly prisma: PrismaService, + private readonly emailResolver : EmailToIdResolver, + ) { } //----------------------------------------------------------------------------------- // GET TIMESHEETS FOR A SELECTED EMPLOYEE //----------------------------------------------------------------------------------- - async getTimesheetsForEmployeeByPeriod(employee_id: number, pay_year: number, pay_period_no: number) { + async getTimesheetsForEmployeeByPeriod(email: string, pay_year: number, pay_period_no: number) { //find period using year and period_no const period = await this.prisma.payPeriods.findFirst({ where: { pay_year, pay_period_no } }); if (!period) throw new NotFoundException(`Pay period ${pay_year}-${pay_period_no} not found`); + //fetch the employee_id using the email + const employee_id = await this.emailResolver.findIdByEmail(email); //loads the timesheets related to the fetched pay-period const timesheet_range = { employee_id, start_date: { gte: period.period_start, lte: period.period_end } }; let rows = await this.loadTimesheets(timesheet_range); From c274550a91dfd44bfae2a58e29fe2e59ed5ae9ca Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 3 Nov 2025 10:15:40 -0500 Subject: [PATCH 29/57] refactor(shifts): removed email from param of create shift and used req-user data instead --- .../shifts/controllers/shift.controller.ts | 8 +- .../shifts/services/shifts-upsert.service.ts | 151 +++++++++++++----- 2 files changed, 114 insertions(+), 45 deletions(-) diff --git a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts index 6928cc8..e26891b 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Body, Controller, Delete, Param, Patch, Post } from "@nestjs/common"; +import { BadRequestException, Body, Controller, Delete, Param, Patch, Post, Req } from "@nestjs/common"; import { CreateShiftResult, UpdateShiftResult } from "src/time-and-attendance/utils/type.utils"; import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; @@ -10,11 +10,13 @@ export class ShiftController { constructor( private readonly upsert_service: ShiftsUpsertService ){} @Post('create') - createBatch( + createBatch( + @Req() req, @Body()dtos: ShiftDto[]): Promise { + const email = req.user?.email; const list = Array.isArray(dtos) ? dtos : []; if(list.length === 0) throw new BadRequestException('Body is missing or invalid (create shifts)'); - return this.upsert_service.createShifts(dtos) + return this.upsert_service.createShifts(email, dtos) } diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts index e16797c..5a1e6c4 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts @@ -1,5 +1,5 @@ import { CreateShiftResult, NormedOk, NormedErr, UpdateShiftResult, UpdateShiftPayload, UpdateShiftChanges, Normalized } from "src/time-and-attendance/utils/type.utils"; -import { overlaps, toStringFromHHmm, toStringFromDate, toDateFromString, toHHmmFromString } from "src/time-and-attendance/utils/date-time.utils"; +import { overlaps, toStringFromHHmm, toStringFromDate, toDateFromString, toHHmmFromString, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils"; import { Injectable, BadRequestException, ConflictException, NotFoundException } from "@nestjs/common"; import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; @@ -7,6 +7,7 @@ import { PrismaService } from "src/prisma/prisma.service"; import { shift_select } from "src/time-and-attendance/utils/selects.utils"; import { GetShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto"; import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; @@ -15,6 +16,7 @@ export class ShiftsUpsertService { constructor( private readonly prisma: PrismaService, private readonly overtime: OvertimeService, + private readonly emailResolver: EmailToIdResolver, ) { } //_________________________________________________________________ @@ -25,76 +27,140 @@ export class ShiftsUpsertService { //checks for overlaping shifts //create new shifts //calculate overtime - async createShifts(dtos: ShiftDto[]): Promise { + async createShifts(email: string, dtos: ShiftDto[]): Promise { if (!Array.isArray(dtos) || dtos.length === 0) return []; - const normed_shift: Array = dtos.map((dto, index) => { - try { - const normed = this.normalizeShiftDto(dto); - if (normed.end_time <= normed.start_time) { - return { index, error: new BadRequestException(`end_time must be greater than start_time (index ${index})`) }; + const employee_id = await this.emailResolver.findIdByEmail(email); + + const normed_shifts = await Promise.all( + dtos.map(async (dto, index) => { + try { + const normed = this.normalizeShiftDto(dto); + if (normed.end_time <= normed.start_time) { + return { + index, + error: new BadRequestException( + `end_time must be greater than start_time (index ${index})` + ), + }; + } + + const start_date = weekStartSunday(normed.date); + + const timesheet = await this.prisma.timesheets.findFirst({ + where: { start_date, employee_id }, + select: { id: true }, + }); + if (!timesheet) { + return { + index, + error: new NotFoundException(`Timesheet not found`), + }; + + } + + return { + index, + dto, + normed, + timesheet_id: timesheet.id, + }; + } catch (error) { + return { index, error }; } - return { index, dto, normed }; - } catch (error) { - return { index, error }; - } - }); - const ok_items = normed_shift.filter((x): x is NormedOk => "normed" in x); + })); - const regroup_by_date = new Map(); + const ok_items = normed_shifts.filter( + (item): item is NormedOk & { timesheet_id: number } => "normed" in item); - ok_items.forEach(({ index, normed }) => { - const d = normed.date; - const key = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); + const regroup_by_date = new Map(); + ok_items.forEach(({ index, normed, timesheet_id }) => { + const day = new Date(normed.date.getFullYear(), normed.date.getMonth(), normed.date.getDate()).getTime(); + const key = `${timesheet_id}|${day}`; if (!regroup_by_date.has(key)) regroup_by_date.set(key, []); regroup_by_date.get(key)!.push(index); }); + const timesheet_keys = Array.from(regroup_by_date.keys()).map((raw) => { + const [timesheet, day] = raw.split('|'); + return { + timesheet_id: Number(timesheet), + day: Number(day), + key: raw, + }; + }); + for (const indices of regroup_by_date.values()) { const ordered = indices .map(index => { - const item = normed_shift[index] as NormedOk; - return { index: index, start: item.normed.start_time, end: item.normed.end_time }; + const item = normed_shifts[index] as NormedOk & { timesheet_id: number }; + return { + index: index, + start: item.normed.start_time, + end: item.normed.end_time + }; }) .sort((a, b) => a.start.getTime() - b.start.getTime()); for (let j = 1; j < ordered.length; j++) { - if (overlaps({ start: ordered[j - 1].start, end: ordered[j - 1].end }, { start: ordered[j].start, end: ordered[j].end })) { - const err = new ConflictException({ + if ( + overlaps( + { start: ordered[j - 1].start, end: ordered[j - 1].end }, + { start: ordered[j].start, end: ordered[j].end } + ) + ) { + const error = new ConflictException({ error_code: 'SHIFT_OVERLAP_BATCH', message: 'New shift overlaps with another shift in the same batch (same day).', }); return dtos.map((_dto, key) => indices.includes(key) - ? ({ ok: false, error: err } as CreateShiftResult) - : ({ ok: false, error: new BadRequestException('Batch aborted due to overlaps in another date group') }) + ? ({ + ok: false, + error + } as CreateShiftResult) + : ({ + ok: false, + error: new BadRequestException( + 'Batch aborted due to overlaps in another date group' + ), + }), ); } } } return this.prisma.$transaction(async (tx) => { - const results: CreateShiftResult[] = Array.from({ length: dtos.length }, () => ({ ok: false, error: new Error('uninitialized') })); + const results: CreateShiftResult[] = Array.from( + { length: dtos.length }, + () => ({ ok: false, error: new Error('uninitialized') })); + const existing_map = new Map(); - normed_shift.forEach((x, i) => { + for (const { timesheet_id, day, key } of timesheet_keys) { + const day_date = new Date(day); + const rows = await tx.shifts.findMany({ + where: { timesheet_id, date: day_date }, + select: { start_time: true, end_time: true }, + }); + existing_map.set( + key, + rows.map((row) => ({ start_time: row.start_time, end_time: row.end_time })), + ); + } + + normed_shifts.forEach((x, i) => { if ("error" in x) results[i] = { ok: false, error: x.error }; }); - const unique_dates = Array.from(regroup_by_date.keys()).map(ms => new Date(ms)); - const existing_date = new Map(); - for (const d of unique_dates) { - const rows = await tx.shifts.findMany({ - where: { date: d }, - select: { start_time: true, end_time: true }, - }); - existing_date.set(d.getTime(), rows.map(r => ({ start_time: r.start_time, end_time: r.end_time }))); - } - for (const item of ok_items) { - const { index, dto, normed } = item; - const dayKey = new Date(normed.date.getFullYear(), normed.date.getMonth(), normed.date.getDate()).getTime(); - const existing = existing_date.get(dayKey) ?? []; - + const { index, dto, normed, timesheet_id } = item; + const day_key = new Date(normed.date.getFullYear(), normed.date.getMonth(), normed.date.getDate()).getTime(); + const map_key = `${timesheet_id}|${day_key}`; + let existing = existing_map.get(map_key); + if(!existing) { + existing = []; + existing_map.set(map_key, existing); + } const hit = existing.find(e => overlaps({ start: e.start_time, end: e.end_time }, { start: normed.start_time, end: normed.end_time })); if (hit) { results[index] = { @@ -114,7 +180,7 @@ export class ShiftsUpsertService { const row = await tx.shifts.create({ data: { - timesheet_id: dto.timesheet_id, + timesheet_id: timesheet_id, bank_code_id: dto.bank_code_id, date: normed.date, start_time: normed.start_time, @@ -126,10 +192,11 @@ export class ShiftsUpsertService { }); existing.push({ start_time: row.start_time, end_time: row.end_time }); + existing_map.set(map_key, existing); - const summary = await this.overtime.getWeekOvertimeSummary(dto.timesheet_id, normed.date, tx); + const summary = await this.overtime.getWeekOvertimeSummary(timesheet_id, normed.date, tx); const shift: GetShiftDto = { - timesheet_id: row.timesheet_id, + timesheet_id: timesheet_id, bank_code_id: row.bank_code_id, date: toStringFromDate(row.date), start_time: toStringFromHHmm(row.start_time), From b1c6c50571735ca9b850791ee88b4899a5060cd1 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 3 Nov 2025 10:53:15 -0500 Subject: [PATCH 30/57] refactor(time-and-attendance): removed a layer of folder --- package-lock.json | 49 +++++++++++++------ src/app.module.ts | 2 +- src/modules/exports/csv-exports.module.ts | 2 +- .../controllers/expense.controller.ts | 7 ++- .../expenses/dtos/expense-create.dto.ts | 0 .../expenses/dtos/expense-get.dto.ts | 0 .../expenses/dtos/expense-update.dto.ts | 2 +- .../expenses/expenses.module.ts | 10 ++++ .../services/expense-upsert.service.ts | 6 +-- .../services/expenses-archival.service.ts | 0 .../controllers/leave-requests.controller.ts | 3 +- .../dtos/leave-request-view.dto.ts | 0 .../dtos/upsert-leave-request.dto.ts | 0 .../leave-requests/leave-requests.module.ts | 9 ++-- .../mappers/leave-requests-archive.mapper.ts | 4 +- .../mappers/leave-requests.mapper.ts | 0 .../holiday-leave-requests.service.ts | 15 +++--- .../services/leave-request.service.ts | 20 ++++---- .../services/sick-leave-requests.service.ts | 19 +++---- .../vacation-leave-requests.service.ts | 18 +++---- .../utils/leave-request.transform.ts | 20 ++++++++ .../utils/leave-request.util.ts | 0 .../utils/leave-requests-archive.select.ts | 0 .../modules/expenses/expenses.module.ts | 10 ---- .../utils/leave-request.transform.ts | 19 ------- .../dtos/update-schedule-presets.dto.ts | 3 -- .../time-tracker/shifts/shifts.module.ts | 14 ------ .../controllers/pay-periods.controller.ts | 0 .../pay-period/dtos/bulk-crew-approval.dto.ts | 0 .../pay-period/dtos/bundle-pay-period.dto.ts | 0 .../dtos/overview-employee-period.dto.ts | 0 .../dtos/overview-pay-period.dto.ts | 0 .../pay-period/dtos/pay-period.dto.ts | 0 .../pay-period/mappers/pay-periods.mapper.ts | 0 .../pay-period/pay-periods.module.ts | 0 .../services/pay-periods-command.service.ts | 0 .../services/pay-periods-query.service.ts | 0 .../shared/helpers/date-time.helpers.ts | 0 .../shared/interfaces/shifts.interface.ts | 0 .../shared/selects/expenses.select.ts | 0 .../shared/selects/pay-periods.select.ts | 0 .../shared/selects/shifts.select.ts | 0 .../{modules => }/shared/shared.module.ts | 0 .../utils/resolve-bank-type-id.utils.ts | 0 .../shared/utils/resolve-email-id.utils.ts | 0 .../shared/utils/resolve-full-name.utils.ts | 0 .../shared/utils/resolve-shifts-id.utils.ts | 0 .../shared/utils/resolve-timesheet.utils.ts | 0 .../time-and-attendance.module.ts | 29 +++++------ .../controller/schedule-presets.controller.ts | 11 +++-- .../dtos/create-schedule-preset-shifts.dto.ts | 0 .../dtos/create-schedule-presets.dto.ts | 2 +- .../dtos/update-schedule-presets.dto.ts | 4 ++ .../schedule-presets.module.ts | 11 +++-- .../schedule-presets-apply.service.ts | 0 .../services/schedule-presets-get.service.ts | 0 .../schedule-presets-upsert.service.ts | 4 +- .../shifts/controllers/shift.controller.ts | 6 +-- .../shifts/dtos/shift-create.dto.ts | 0 .../time-tracker/shifts/dtos/shift-get.dto.ts | 0 .../shifts/dtos/shift-update.dto.ts | 2 +- .../services/shifts-archival.service.ts | 0 .../shifts/services/shifts-get.service.ts | 2 +- .../shifts/services/shifts-upsert.service.ts | 8 +-- .../time-tracker/shifts/shifts.module.ts | 14 ++++++ .../controllers/timesheet.controller.ts | 2 +- .../timesheets/dtos/timesheet.dto.ts | 0 .../services/timesheet-approval.service.ts | 0 .../services/timesheet-archive.service.ts | 0 .../timesheet-get-overview.service.ts | 2 +- .../timesheets/timesheets.module.ts | 9 ++-- src/time-and-attendance/utils/type.utils.ts | 17 ++++--- 72 files changed, 190 insertions(+), 165 deletions(-) rename src/time-and-attendance/{modules => }/expenses/controllers/expense.controller.ts (75%) rename src/time-and-attendance/{modules => }/expenses/dtos/expense-create.dto.ts (100%) rename src/time-and-attendance/{modules => }/expenses/dtos/expense-get.dto.ts (100%) rename src/time-and-attendance/{modules => }/expenses/dtos/expense-update.dto.ts (65%) create mode 100644 src/time-and-attendance/expenses/expenses.module.ts rename src/time-and-attendance/{modules => }/expenses/services/expense-upsert.service.ts (96%) rename src/time-and-attendance/{modules => }/expenses/services/expenses-archival.service.ts (100%) rename src/time-and-attendance/{modules => }/leave-requests/controllers/leave-requests.controller.ts (94%) rename src/time-and-attendance/{modules => }/leave-requests/dtos/leave-request-view.dto.ts (100%) rename src/time-and-attendance/{modules => }/leave-requests/dtos/upsert-leave-request.dto.ts (100%) rename src/time-and-attendance/{modules => }/leave-requests/leave-requests.module.ts (54%) rename src/time-and-attendance/{modules => }/leave-requests/mappers/leave-requests-archive.mapper.ts (100%) rename src/time-and-attendance/{modules => }/leave-requests/mappers/leave-requests.mapper.ts (100%) rename src/time-and-attendance/{modules => }/leave-requests/services/holiday-leave-requests.service.ts (82%) rename src/time-and-attendance/{modules => }/leave-requests/services/leave-request.service.ts (96%) rename src/time-and-attendance/{modules => }/leave-requests/services/sick-leave-requests.service.ts (88%) rename src/time-and-attendance/{modules => }/leave-requests/services/vacation-leave-requests.service.ts (88%) create mode 100644 src/time-and-attendance/leave-requests/utils/leave-request.transform.ts rename src/time-and-attendance/{modules => }/leave-requests/utils/leave-request.util.ts (100%) rename src/time-and-attendance/{modules => }/leave-requests/utils/leave-requests-archive.select.ts (100%) delete mode 100644 src/time-and-attendance/modules/expenses/expenses.module.ts delete mode 100644 src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts delete mode 100644 src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts delete mode 100644 src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts rename src/time-and-attendance/{modules => }/pay-period/controllers/pay-periods.controller.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/dtos/bulk-crew-approval.dto.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/dtos/bundle-pay-period.dto.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/dtos/overview-employee-period.dto.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/dtos/overview-pay-period.dto.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/dtos/pay-period.dto.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/mappers/pay-periods.mapper.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/pay-periods.module.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/services/pay-periods-command.service.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/services/pay-periods-query.service.ts (100%) rename src/time-and-attendance/{modules => }/shared/helpers/date-time.helpers.ts (100%) rename src/time-and-attendance/{modules => }/shared/interfaces/shifts.interface.ts (100%) rename src/time-and-attendance/{modules => }/shared/selects/expenses.select.ts (100%) rename src/time-and-attendance/{modules => }/shared/selects/pay-periods.select.ts (100%) rename src/time-and-attendance/{modules => }/shared/selects/shifts.select.ts (100%) rename src/time-and-attendance/{modules => }/shared/shared.module.ts (100%) rename src/time-and-attendance/{modules => }/shared/utils/resolve-bank-type-id.utils.ts (100%) rename src/time-and-attendance/{modules => }/shared/utils/resolve-email-id.utils.ts (100%) rename src/time-and-attendance/{modules => }/shared/utils/resolve-full-name.utils.ts (100%) rename src/time-and-attendance/{modules => }/shared/utils/resolve-shifts-id.utils.ts (100%) rename src/time-and-attendance/{modules => }/shared/utils/resolve-timesheet.utils.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/schedule-presets/controller/schedule-presets.controller.ts (83%) rename src/time-and-attendance/{modules => }/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts (82%) create mode 100644 src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts rename src/time-and-attendance/{modules => }/time-tracker/schedule-presets/schedule-presets.module.ts (62%) rename src/time-and-attendance/{modules => }/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/schedule-presets/services/schedule-presets-get.service.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts (97%) rename src/time-and-attendance/{modules => }/time-tracker/shifts/controllers/shift.controller.ts (78%) rename src/time-and-attendance/{modules => }/time-tracker/shifts/dtos/shift-create.dto.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/shifts/dtos/shift-get.dto.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/shifts/dtos/shift-update.dto.ts (76%) rename src/time-and-attendance/{modules => }/time-tracker/shifts/services/shifts-archival.service.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/shifts/services/shifts-get.service.ts (95%) rename src/time-and-attendance/{modules => }/time-tracker/shifts/services/shifts-upsert.service.ts (97%) create mode 100644 src/time-and-attendance/time-tracker/shifts/shifts.module.ts rename src/time-and-attendance/{modules => }/time-tracker/timesheets/controllers/timesheet.controller.ts (90%) rename src/time-and-attendance/{modules => }/time-tracker/timesheets/dtos/timesheet.dto.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/timesheets/services/timesheet-approval.service.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/timesheets/services/timesheet-archive.service.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/timesheets/services/timesheet-get-overview.service.ts (99%) rename src/time-and-attendance/{modules => }/time-tracker/timesheets/timesheets.module.ts (50%) diff --git a/package-lock.json b/package-lock.json index b3363fc..9b49166 100644 --- a/package-lock.json +++ b/package-lock.json @@ -243,6 +243,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -3113,6 +3114,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -3271,6 +3273,7 @@ "version": "11.1.7", "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.7.tgz", "integrity": "sha512-lwlObwGgIlpXSXYOTpfzdCepUyWomz6bv9qzGzzvpgspUxkj0Uz0fUJcvD44V8Ps7QhKW3lZBoYbXrH25UZrbA==", + "peer": true, "dependencies": { "file-type": "21.0.0", "iterare": "1.2.1", @@ -3316,6 +3319,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.7.tgz", "integrity": "sha512-TyXFOwjhHv/goSgJ8i20K78jwTM0iSpk9GBcC2h3mf4MxNy+znI8m7nWjfoACjTkb89cTwDQetfTHtSfGLLaiA==", "hasInstallScript": true, + "peer": true, "dependencies": { "@nuxt/opencollective": "0.4.1", "fast-safe-stringify": "2.1.1", @@ -3395,6 +3399,7 @@ "version": "11.1.7", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.7.tgz", "integrity": "sha512-5T+GLdvTiGPKB4/P4PM9ftKUKNHJy8ThEFhZA3vQnXVL7Vf0rDr07TfVTySVu+XTh85m1lpFVuyFM6u6wLNsRA==", + "peer": true, "dependencies": { "cors": "2.8.5", "express": "5.1.0", @@ -3794,6 +3799,7 @@ "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.6.0.tgz", "integrity": "sha512-Q5FsI3Cw0fGMXhmsg7c08i4EmXCrcl+WnAxb6LYOLHw4JFFC3yzmx9LaXZ7QMbA+JZXbigU2TirI7RAfO0Qlnw==", "dev": true, + "peer": true, "dependencies": { "@swc/counter": "^0.1.3", "@xhmikosr/bin-wrapper": "^13.0.5", @@ -3862,6 +3868,7 @@ "integrity": "sha512-CJSn2vstd17ddWIHBsjuD4OQnn9krQfaq6EO+w9YfId5DKznyPmzxAARlOXG99cC8/3Kli8ysKy6phL43bSr0w==", "dev": true, "hasInstallScript": true, + "peer": true, "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" @@ -4198,6 +4205,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -4357,6 +4365,7 @@ "version": "22.17.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.2.tgz", "integrity": "sha512-gL6z5N9Jm9mhY+U2KXZpteb+09zyffliRkZyZOHODGATyC5B1Jt/7TzuuiLkFsSUMLbS1OLmlj/E+/3KF4Q/4w==", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -4537,6 +4546,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.37.0", "@typescript-eslint/types": "8.37.0", @@ -5440,6 +5450,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5452,7 +5463,6 @@ "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, - "peer": true, "engines": { "node": ">=10.13.0" }, @@ -5486,6 +5496,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5953,6 +5964,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", @@ -6231,6 +6243,7 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "devOptional": true, + "peer": true, "dependencies": { "readdirp": "^4.0.1" }, @@ -6283,12 +6296,14 @@ "node_modules/class-transformer": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "peer": true }, "node_modules/class-validator": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.2.tgz", "integrity": "sha512-3kMVRF2io8N8pY1IFIXlho9r8IPUUIfHe2hYVtiebvAzU2XeQFXTv+XI4WX+TnXmtwXMDcjngcpkiPM0O9PvLw==", + "peer": true, "dependencies": { "@types/validator": "^13.11.8", "libphonenumber-js": "^1.11.1", @@ -7151,6 +7166,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -7211,6 +7227,7 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", "dev": true, + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -8717,6 +8734,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -10353,6 +10371,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "peer": true, "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1", @@ -10646,6 +10665,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -10700,6 +10720,7 @@ "integrity": "sha512-bXWy3vTk8mnRmT+SLyZBQoC2vtV9Z8u7OHvEu+aULYxwiop/CPiFZ+F56KsNRNf35jw+8wcu8pmLsjxpBxAO9g==", "devOptional": true, "hasInstallScript": true, + "peer": true, "dependencies": { "@prisma/config": "6.18.0", "@prisma/engines": "6.18.0" @@ -10919,7 +10940,8 @@ "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "peer": true }, "node_modules/repeat-string": { "version": "1.6.1", @@ -11108,6 +11130,7 @@ "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -11917,6 +11940,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -12225,6 +12249,7 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -12382,6 +12407,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "devOptional": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12564,9 +12590,10 @@ } }, "node_modules/validator": { - "version": "13.15.15", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", - "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", + "version": "13.15.20", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz", + "integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==", + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -12711,7 +12738,6 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, - "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -12729,7 +12755,6 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -12742,7 +12767,6 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -12756,7 +12780,6 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "peer": true, "engines": { "node": ">=4.0" } @@ -12765,15 +12788,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "peer": true + "dev": true }, "node_modules/webpack/node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, - "peer": true, "engines": { "node": ">= 0.6" } @@ -12783,7 +12804,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -12796,7 +12816,6 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", "dev": true, - "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", diff --git a/src/app.module.ts b/src/app.module.ts index ae2e569..dbe9dd9 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -14,8 +14,8 @@ import { ConfigModule } from '@nestjs/config'; import { APP_FILTER, APP_PIPE } from '@nestjs/core'; import { HttpExceptionFilter } from './common/filters/http-exception.filter'; import { ValidationError } from 'class-validator'; -import { PayperiodsModule } from './time-and-attendance/modules/pay-period/pay-periods.module'; import { TimeAndAttendanceModule } from 'src/time-and-attendance/time-and-attendance.module'; +import { PayperiodsModule } from 'src/time-and-attendance/pay-period/pay-periods.module'; @Module({ imports: [ diff --git a/src/modules/exports/csv-exports.module.ts b/src/modules/exports/csv-exports.module.ts index fa0c7f1..30a9b8d 100644 --- a/src/modules/exports/csv-exports.module.ts +++ b/src/modules/exports/csv-exports.module.ts @@ -1,7 +1,7 @@ import { Module } from "@nestjs/common"; import { CsvExportController } from "./controllers/csv-exports.controller"; import { CsvExportService } from "./services/csv-exports.service"; -import { SharedModule } from "../../time-and-attendance/modules/shared/shared.module"; +import { SharedModule } from "src/time-and-attendance/shared/shared.module"; @Module({ providers:[CsvExportService, SharedModule], diff --git a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts b/src/time-and-attendance/expenses/controllers/expense.controller.ts similarity index 75% rename from src/time-and-attendance/modules/expenses/controllers/expense.controller.ts rename to src/time-and-attendance/expenses/controllers/expense.controller.ts index 594d583..34b83e9 100644 --- a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/expenses/controllers/expense.controller.ts @@ -1,9 +1,8 @@ import { Controller, Post, Param, Body, Patch, Delete, Req, UnauthorizedException } from "@nestjs/common"; import { CreateExpenseResult, UpdateExpenseResult } from "src/time-and-attendance/utils/type.utils"; -import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; -import { updateExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-update.dto"; -import { ExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-create.dto"; - +import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service"; +import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto"; +import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; @Controller('expense') export class ExpenseController { diff --git a/src/time-and-attendance/modules/expenses/dtos/expense-create.dto.ts b/src/time-and-attendance/expenses/dtos/expense-create.dto.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/dtos/expense-create.dto.ts rename to src/time-and-attendance/expenses/dtos/expense-create.dto.ts diff --git a/src/time-and-attendance/modules/expenses/dtos/expense-get.dto.ts b/src/time-and-attendance/expenses/dtos/expense-get.dto.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/dtos/expense-get.dto.ts rename to src/time-and-attendance/expenses/dtos/expense-get.dto.ts diff --git a/src/time-and-attendance/modules/expenses/dtos/expense-update.dto.ts b/src/time-and-attendance/expenses/dtos/expense-update.dto.ts similarity index 65% rename from src/time-and-attendance/modules/expenses/dtos/expense-update.dto.ts rename to src/time-and-attendance/expenses/dtos/expense-update.dto.ts index 829f9c9..3dfe9b6 100644 --- a/src/time-and-attendance/modules/expenses/dtos/expense-update.dto.ts +++ b/src/time-and-attendance/expenses/dtos/expense-update.dto.ts @@ -1,5 +1,5 @@ import { OmitType, PartialType } from "@nestjs/swagger"; -import { ExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-create.dto"; +import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; export class updateExpenseDto extends PartialType ( OmitType(ExpenseDto, ['is_approved', 'timesheet_id'] as const) diff --git a/src/time-and-attendance/expenses/expenses.module.ts b/src/time-and-attendance/expenses/expenses.module.ts new file mode 100644 index 0000000..e6d737b --- /dev/null +++ b/src/time-and-attendance/expenses/expenses.module.ts @@ -0,0 +1,10 @@ +import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service"; +import { ExpenseController } from "src/time-and-attendance/expenses/controllers/expense.controller"; +import { Module } from "@nestjs/common"; + +@Module({ + controllers: [ ExpenseController ], + providers: [ ExpenseUpsertService ], +}) + +export class ExpensesModule {} \ No newline at end of file diff --git a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/expenses/services/expense-upsert.service.ts similarity index 96% rename from src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts rename to src/time-and-attendance/expenses/services/expense-upsert.service.ts index d2c4cb7..86a1c9b 100644 --- a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/expenses/services/expense-upsert.service.ts @@ -4,9 +4,9 @@ import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-an import { Injectable, NotFoundException, Req } from "@nestjs/common"; import { expense_select } from "src/time-and-attendance/utils/selects.utils"; import { PrismaService } from "src/prisma/prisma.service"; -import { GetExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-get.dto"; -import { ExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-create.dto"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; +import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; @Injectable() diff --git a/src/time-and-attendance/modules/expenses/services/expenses-archival.service.ts b/src/time-and-attendance/expenses/services/expenses-archival.service.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/services/expenses-archival.service.ts rename to src/time-and-attendance/expenses/services/expenses-archival.service.ts diff --git a/src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller.ts b/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts similarity index 94% rename from src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller.ts rename to src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts index fc934ff..53c8e47 100644 --- a/src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller.ts +++ b/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts @@ -1,8 +1,7 @@ import { Body, Controller, Post } from "@nestjs/common"; import { ApiBearerAuth, ApiTags } from "@nestjs/swagger"; -import { LeaveRequestsService } from "../services/leave-request.service"; import { UpsertLeaveRequestDto } from "../dtos/upsert-leave-request.dto"; -import { LeaveTypes } from "@prisma/client"; +import { LeaveRequestsService } from "../services/leave-request.service"; @ApiTags('Leave Requests') @ApiBearerAuth('access-token') diff --git a/src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto.ts b/src/time-and-attendance/leave-requests/dtos/leave-request-view.dto.ts similarity index 100% rename from src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto.ts rename to src/time-and-attendance/leave-requests/dtos/leave-request-view.dto.ts diff --git a/src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto.ts b/src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto.ts similarity index 100% rename from src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto.ts rename to src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto.ts diff --git a/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts b/src/time-and-attendance/leave-requests/leave-requests.module.ts similarity index 54% rename from src/time-and-attendance/modules/leave-requests/leave-requests.module.ts rename to src/time-and-attendance/leave-requests/leave-requests.module.ts index 21b9e23..a822a27 100644 --- a/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts +++ b/src/time-and-attendance/leave-requests/leave-requests.module.ts @@ -1,11 +1,10 @@ -import { LeaveRequestController } from "src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller"; -import { LeaveRequestsService } from "src/time-and-attendance/modules/leave-requests/services/leave-request.service"; +import { LeaveRequestController } from "src/time-and-attendance/leave-requests/controllers/leave-requests.controller"; +import { LeaveRequestsService } from "src/time-and-attendance/leave-requests/services/leave-request.service"; import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; -import { ShiftsModule } from "src/time-and-attendance/modules/time-tracker/shifts/shifts.module"; -import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; +import { SharedModule } from "src/time-and-attendance/shared/shared.module"; +import { ShiftsModule } from "src/time-and-attendance/time-tracker/shifts/shifts.module"; import { Module } from "@nestjs/common"; - @Module({ imports: [ BusinessLogicsModule, diff --git a/src/time-and-attendance/modules/leave-requests/mappers/leave-requests-archive.mapper.ts b/src/time-and-attendance/leave-requests/mappers/leave-requests-archive.mapper.ts similarity index 100% rename from src/time-and-attendance/modules/leave-requests/mappers/leave-requests-archive.mapper.ts rename to src/time-and-attendance/leave-requests/mappers/leave-requests-archive.mapper.ts index 36d05fa..6a17723 100644 --- a/src/time-and-attendance/modules/leave-requests/mappers/leave-requests-archive.mapper.ts +++ b/src/time-and-attendance/leave-requests/mappers/leave-requests-archive.mapper.ts @@ -1,6 +1,6 @@ -import { Prisma } from "@prisma/client"; -import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; import { LeaveRequestArchiveRow } from "../utils/leave-requests-archive.select"; +import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; +import { Prisma } from "@prisma/client"; const toNum = (value?: Prisma.Decimal | null) => value ? Number(value) : undefined; diff --git a/src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper.ts b/src/time-and-attendance/leave-requests/mappers/leave-requests.mapper.ts similarity index 100% rename from src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper.ts rename to src/time-and-attendance/leave-requests/mappers/leave-requests.mapper.ts diff --git a/src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts similarity index 82% rename from src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts rename to src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts index 148f4bf..5991686 100644 --- a/src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts @@ -1,15 +1,16 @@ import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; -import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto"; +import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto"; import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; -import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; -import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; -import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; -import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; +import { LeaveRequestsUtils } from "src/time-and-attendance/leave-requests/utils/leave-request.util"; +import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; import { PrismaService } from "src/prisma/prisma.service"; -import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper"; +import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; + @Injectable() diff --git a/src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts b/src/time-and-attendance/leave-requests/services/leave-request.service.ts similarity index 96% rename from src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts rename to src/time-and-attendance/leave-requests/services/leave-request.service.ts index 0fd0ceb..6390a10 100644 --- a/src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts +++ b/src/time-and-attendance/leave-requests/services/leave-request.service.ts @@ -1,18 +1,18 @@ import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; -import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; -import { roundToQuarterHour } from "src/common/utils/date-utils"; +import { normalizeDates, toDateOnly, toISODateKey } from "src/time-and-attendance/shared/helpers/date-time.helpers"; import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto"; +import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; +import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; -import { mapRowToView } from "../mappers/leave-requests.mapper"; -import { PrismaService } from "src/prisma/prisma.service"; -import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; +import { roundToQuarterHour } from "src/common/utils/date-utils"; +import { LeaveRequestsUtils } from "src/time-and-attendance/leave-requests/utils/leave-request.util"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; -import { normalizeDates, toDateOnly, toISODateKey } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; -import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; -import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; -import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; +import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; +import { PrismaService } from "src/prisma/prisma.service"; +import { mapRowToView } from "../mappers/leave-requests.mapper"; @Injectable() export class LeaveRequestsService { constructor( diff --git a/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts similarity index 88% rename from src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts rename to src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts index 969d8a5..629dbff 100644 --- a/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts @@ -1,15 +1,16 @@ import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; +import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto"; import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; -import { roundToQuarterHour } from "src/common/utils/date-utils"; -import { PrismaService } from "src/prisma/prisma.service"; -import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; -import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto"; -import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto"; -import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper"; -import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; -import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers"; +import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; +import { roundToQuarterHour } from "src/common/utils/date-utils"; +import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; +import { PrismaService } from "src/prisma/prisma.service"; +import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; + @Injectable() diff --git a/src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts similarity index 88% rename from src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts rename to src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts index 9f0afc7..d6f16a5 100644 --- a/src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts @@ -1,15 +1,15 @@ import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; +import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto"; import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; -import { roundToQuarterHour } from "src/common/utils/date-utils"; -import { PrismaService } from "src/prisma/prisma.service"; -import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; -import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto"; -import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto"; -import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper"; -import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; -import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; +import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; +import { roundToQuarterHour } from "src/common/utils/date-utils"; +import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; +import { PrismaService } from "src/prisma/prisma.service"; +import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; @Injectable() diff --git a/src/time-and-attendance/leave-requests/utils/leave-request.transform.ts b/src/time-and-attendance/leave-requests/utils/leave-request.transform.ts new file mode 100644 index 0000000..5ff49fd --- /dev/null +++ b/src/time-and-attendance/leave-requests/utils/leave-request.transform.ts @@ -0,0 +1,20 @@ +import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; +import { mapArchiveRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests-archive.mapper"; +import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; +import { LeaveRequestArchiveRow } from "src/time-and-attendance/leave-requests/utils/leave-requests-archive.select"; +import { LeaveRequestRow } from "src/time-and-attendance/utils/type.utils"; + + +/** Active (table leave_requests) : proxy to base mapper */ +export function mapRowToViewWithDays(row: LeaveRequestRow): LeaveRequestViewDto { + return mapRowToView(row); +} + +/** Archive (table leave_requests_archive) : proxy to base mapper */ +export function mapArchiveRowToViewWithDays( + row: LeaveRequestArchiveRow, + email: string, + employee_full_name?: string, +): LeaveRequestViewDto { + return mapArchiveRowToView(row, email, employee_full_name!); +} \ No newline at end of file diff --git a/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts b/src/time-and-attendance/leave-requests/utils/leave-request.util.ts similarity index 100% rename from src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts rename to src/time-and-attendance/leave-requests/utils/leave-request.util.ts diff --git a/src/time-and-attendance/modules/leave-requests/utils/leave-requests-archive.select.ts b/src/time-and-attendance/leave-requests/utils/leave-requests-archive.select.ts similarity index 100% rename from src/time-and-attendance/modules/leave-requests/utils/leave-requests-archive.select.ts rename to src/time-and-attendance/leave-requests/utils/leave-requests-archive.select.ts diff --git a/src/time-and-attendance/modules/expenses/expenses.module.ts b/src/time-and-attendance/modules/expenses/expenses.module.ts deleted file mode 100644 index 6ffff02..0000000 --- a/src/time-and-attendance/modules/expenses/expenses.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; -import { ExpenseController } from "src/time-and-attendance/modules/expenses/controllers/expense.controller"; -import { Module } from "@nestjs/common"; - -@Module({ - controllers: [ ExpenseController ], - providers: [ ExpenseUpsertService ], -}) - -export class ExpensesModule {} \ No newline at end of file diff --git a/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts b/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts deleted file mode 100644 index 042c796..0000000 --- a/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { LeaveRequestArchiveRow } from './leave-requests-archive.select'; -import { LeaveRequestViewDto } from '../dtos/leave-request-view.dto'; -import { mapArchiveRowToView } from '../mappers/leave-requests-archive.mapper'; -import { LeaveRequestRow } from 'src/time-and-attendance/utils/type.utils'; -import { mapRowToView } from '../mappers/leave-requests.mapper'; - -/** Active (table leave_requests) : proxy to base mapper */ -export function mapRowToViewWithDays(row: LeaveRequestRow): LeaveRequestViewDto { - return mapRowToView(row); -} - -/** Archive (table leave_requests_archive) : proxy to base mapper */ -export function mapArchiveRowToViewWithDays( - row: LeaveRequestArchiveRow, - email: string, - employee_full_name?: string, -): LeaveRequestViewDto { - return mapArchiveRowToView(row, email, employee_full_name!); -} \ No newline at end of file diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts deleted file mode 100644 index 2246c20..0000000 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; - -export class SchedulePresetsUpdateDto extends SchedulePresetsDto{} \ No newline at end of file diff --git a/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts b/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts deleted file mode 100644 index e031f31..0000000 --- a/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import { BusinessLogicsModule } from 'src/time-and-attendance/domains/business-logics.module'; -import { ShiftsUpsertService } from 'src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service'; -import { ShiftsGetService } from 'src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service'; -import { ShiftController } from 'src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller'; -import { Module } from '@nestjs/common'; - -@Module({ - imports: [ BusinessLogicsModule ], - controllers: [ShiftController], - providers: [ ShiftsGetService, ShiftsUpsertService ], - exports: [ ShiftsUpsertService ], -}) -export class ShiftsModule {} diff --git a/src/time-and-attendance/modules/pay-period/controllers/pay-periods.controller.ts b/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/controllers/pay-periods.controller.ts rename to src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts diff --git a/src/time-and-attendance/modules/pay-period/dtos/bulk-crew-approval.dto.ts b/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/dtos/bulk-crew-approval.dto.ts rename to src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts diff --git a/src/time-and-attendance/modules/pay-period/dtos/bundle-pay-period.dto.ts b/src/time-and-attendance/pay-period/dtos/bundle-pay-period.dto.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/dtos/bundle-pay-period.dto.ts rename to src/time-and-attendance/pay-period/dtos/bundle-pay-period.dto.ts diff --git a/src/time-and-attendance/modules/pay-period/dtos/overview-employee-period.dto.ts b/src/time-and-attendance/pay-period/dtos/overview-employee-period.dto.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/dtos/overview-employee-period.dto.ts rename to src/time-and-attendance/pay-period/dtos/overview-employee-period.dto.ts diff --git a/src/time-and-attendance/modules/pay-period/dtos/overview-pay-period.dto.ts b/src/time-and-attendance/pay-period/dtos/overview-pay-period.dto.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/dtos/overview-pay-period.dto.ts rename to src/time-and-attendance/pay-period/dtos/overview-pay-period.dto.ts diff --git a/src/time-and-attendance/modules/pay-period/dtos/pay-period.dto.ts b/src/time-and-attendance/pay-period/dtos/pay-period.dto.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/dtos/pay-period.dto.ts rename to src/time-and-attendance/pay-period/dtos/pay-period.dto.ts diff --git a/src/time-and-attendance/modules/pay-period/mappers/pay-periods.mapper.ts b/src/time-and-attendance/pay-period/mappers/pay-periods.mapper.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/mappers/pay-periods.mapper.ts rename to src/time-and-attendance/pay-period/mappers/pay-periods.mapper.ts diff --git a/src/time-and-attendance/modules/pay-period/pay-periods.module.ts b/src/time-and-attendance/pay-period/pay-periods.module.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/pay-periods.module.ts rename to src/time-and-attendance/pay-period/pay-periods.module.ts diff --git a/src/time-and-attendance/modules/pay-period/services/pay-periods-command.service.ts b/src/time-and-attendance/pay-period/services/pay-periods-command.service.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/services/pay-periods-command.service.ts rename to src/time-and-attendance/pay-period/services/pay-periods-command.service.ts diff --git a/src/time-and-attendance/modules/pay-period/services/pay-periods-query.service.ts b/src/time-and-attendance/pay-period/services/pay-periods-query.service.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/services/pay-periods-query.service.ts rename to src/time-and-attendance/pay-period/services/pay-periods-query.service.ts diff --git a/src/time-and-attendance/modules/shared/helpers/date-time.helpers.ts b/src/time-and-attendance/shared/helpers/date-time.helpers.ts similarity index 100% rename from src/time-and-attendance/modules/shared/helpers/date-time.helpers.ts rename to src/time-and-attendance/shared/helpers/date-time.helpers.ts diff --git a/src/time-and-attendance/modules/shared/interfaces/shifts.interface.ts b/src/time-and-attendance/shared/interfaces/shifts.interface.ts similarity index 100% rename from src/time-and-attendance/modules/shared/interfaces/shifts.interface.ts rename to src/time-and-attendance/shared/interfaces/shifts.interface.ts diff --git a/src/time-and-attendance/modules/shared/selects/expenses.select.ts b/src/time-and-attendance/shared/selects/expenses.select.ts similarity index 100% rename from src/time-and-attendance/modules/shared/selects/expenses.select.ts rename to src/time-and-attendance/shared/selects/expenses.select.ts diff --git a/src/time-and-attendance/modules/shared/selects/pay-periods.select.ts b/src/time-and-attendance/shared/selects/pay-periods.select.ts similarity index 100% rename from src/time-and-attendance/modules/shared/selects/pay-periods.select.ts rename to src/time-and-attendance/shared/selects/pay-periods.select.ts diff --git a/src/time-and-attendance/modules/shared/selects/shifts.select.ts b/src/time-and-attendance/shared/selects/shifts.select.ts similarity index 100% rename from src/time-and-attendance/modules/shared/selects/shifts.select.ts rename to src/time-and-attendance/shared/selects/shifts.select.ts diff --git a/src/time-and-attendance/modules/shared/shared.module.ts b/src/time-and-attendance/shared/shared.module.ts similarity index 100% rename from src/time-and-attendance/modules/shared/shared.module.ts rename to src/time-and-attendance/shared/shared.module.ts diff --git a/src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils.ts b/src/time-and-attendance/shared/utils/resolve-bank-type-id.utils.ts similarity index 100% rename from src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils.ts rename to src/time-and-attendance/shared/utils/resolve-bank-type-id.utils.ts diff --git a/src/time-and-attendance/modules/shared/utils/resolve-email-id.utils.ts b/src/time-and-attendance/shared/utils/resolve-email-id.utils.ts similarity index 100% rename from src/time-and-attendance/modules/shared/utils/resolve-email-id.utils.ts rename to src/time-and-attendance/shared/utils/resolve-email-id.utils.ts diff --git a/src/time-and-attendance/modules/shared/utils/resolve-full-name.utils.ts b/src/time-and-attendance/shared/utils/resolve-full-name.utils.ts similarity index 100% rename from src/time-and-attendance/modules/shared/utils/resolve-full-name.utils.ts rename to src/time-and-attendance/shared/utils/resolve-full-name.utils.ts diff --git a/src/time-and-attendance/modules/shared/utils/resolve-shifts-id.utils.ts b/src/time-and-attendance/shared/utils/resolve-shifts-id.utils.ts similarity index 100% rename from src/time-and-attendance/modules/shared/utils/resolve-shifts-id.utils.ts rename to src/time-and-attendance/shared/utils/resolve-shifts-id.utils.ts diff --git a/src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts b/src/time-and-attendance/shared/utils/resolve-timesheet.utils.ts similarity index 100% rename from src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts rename to src/time-and-attendance/shared/utils/resolve-timesheet.utils.ts diff --git a/src/time-and-attendance/time-and-attendance.module.ts b/src/time-and-attendance/time-and-attendance.module.ts index ff54d41..89de655 100644 --- a/src/time-and-attendance/time-and-attendance.module.ts +++ b/src/time-and-attendance/time-and-attendance.module.ts @@ -1,18 +1,19 @@ -import { SchedulePresetsUpsertService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; -import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service"; -import { SchedulePresetsGetService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service"; -import { SchedulePresetsApplyService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service"; -import { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller"; -import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; -import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; -import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; -import { TimesheetController } from "src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller"; -import { ExpenseController } from "src/time-and-attendance/modules/expenses/controllers/expense.controller"; -import { PayperiodsModule } from "src/time-and-attendance/modules/pay-period/pay-periods.module"; -import { ShiftsGetService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service"; -import { ShiftController } from "src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller"; -import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; + import { Module } from "@nestjs/common"; +import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; +import { ExpenseController } from "src/time-and-attendance/expenses/controllers/expense.controller"; +import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service"; +import { PayperiodsModule } from "src/time-and-attendance/pay-period/pay-periods.module"; +import { SharedModule } from "src/time-and-attendance/shared/shared.module"; +import { SchedulePresetsController } from "src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller"; +import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; +import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; +import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; +import { ShiftController } from "src/time-and-attendance/time-tracker/shifts/controllers/shift.controller"; +import { ShiftsGetService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-get.service"; +import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service"; +import { TimesheetController } from "src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller"; +import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service"; @Module({ imports: [ diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts similarity index 83% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts rename to src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index a9d5c87..3a2c692 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -1,9 +1,10 @@ import { Controller, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch } from "@nestjs/common"; -import { SchedulePresetsUpsertService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; -import { SchedulePresetsApplyService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service"; -import { SchedulePresetsGetService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service"; -import { SchedulePresetsUpdateDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto"; -import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; +import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; +import { SchedulePresetsUpdateDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto"; +import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; +import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; +import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; + @Controller('schedule-presets') export class SchedulePresetsController { diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts b/src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts rename to src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts b/src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts similarity index 82% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts rename to src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts index 86d7704..fc6b7d8 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts @@ -1,5 +1,5 @@ import { ArrayMinSize, IsArray, IsBoolean, IsOptional, IsString } from "class-validator"; -import { SchedulePresetShiftsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto"; +import { SchedulePresetShiftsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto"; export class SchedulePresetsDto { @IsString() diff --git a/src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts b/src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts new file mode 100644 index 0000000..b8b9cb6 --- /dev/null +++ b/src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts @@ -0,0 +1,4 @@ +import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; + + +export class SchedulePresetsUpdateDto extends SchedulePresetsDto{} \ No newline at end of file diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts b/src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts similarity index 62% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts rename to src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts index ac6c595..b261a2f 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts @@ -1,9 +1,10 @@ -import { SchedulePresetsUpsertService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; -import { SchedulePresetsApplyService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service"; -import { SchedulePresetsGetService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service"; -import { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller"; -import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; + import { Module } from "@nestjs/common"; +import { SharedModule } from "src/time-and-attendance/shared/shared.module"; +import { SchedulePresetsController } from "src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller"; +import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; +import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; +import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; @Module({ diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts rename to src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service.ts rename to src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts similarity index 97% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts rename to src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts index da45941..ec60558 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts @@ -1,10 +1,10 @@ import { Injectable, BadRequestException, NotFoundException, ConflictException } from "@nestjs/common"; import { CreatePresetResult, DeletePresetResult, UpdatePresetResult } from "src/time-and-attendance/utils/type.utils"; -import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; -import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; import { Prisma, Weekday } from "@prisma/client"; import { toHHmmFromDate } from "src/time-and-attendance/utils/date-time.utils"; import { PrismaService } from "src/prisma/prisma.service"; +import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; +import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; @Injectable() export class SchedulePresetsUpsertService { diff --git a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts similarity index 78% rename from src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts rename to src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts index e26891b..07395f9 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts @@ -1,8 +1,8 @@ import { BadRequestException, Body, Controller, Delete, Param, Patch, Post, Req } from "@nestjs/common"; +import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; +import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; +import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service"; import { CreateShiftResult, UpdateShiftResult } from "src/time-and-attendance/utils/type.utils"; -import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; -import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; -import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; @Controller('shift') diff --git a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto.ts b/src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto.ts rename to src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto.ts b/src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto.ts rename to src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts b/src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto.ts similarity index 76% rename from src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts rename to src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto.ts index ce16f8e..0dd4505 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts +++ b/src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto.ts @@ -1,6 +1,6 @@ import { PartialType, OmitType } from "@nestjs/swagger"; -import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; import { IsInt } from "class-validator"; +import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; export class UpdateShiftDto extends PartialType( // allows update using ShiftDto and preventing OmitType variables to be modified diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-archival.service.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts rename to src/time-and-attendance/time-tracker/shifts/services/shifts-archival.service.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts similarity index 95% rename from src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts rename to src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts index c1c2e12..d1eed96 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts @@ -2,7 +2,7 @@ import { toStringFromDate, toStringFromHHmm } from "src/time-and-attendance/util import { Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { shift_select } from "src/time-and-attendance/utils/selects.utils"; -import { GetShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto"; +import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; /** diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts similarity index 97% rename from src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts rename to src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 5a1e6c4..44261cb 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -2,12 +2,12 @@ import { CreateShiftResult, NormedOk, NormedErr, UpdateShiftResult, UpdateShiftP import { overlaps, toStringFromHHmm, toStringFromDate, toDateFromString, toHHmmFromString, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils"; import { Injectable, BadRequestException, ConflictException, NotFoundException } from "@nestjs/common"; import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; -import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; import { PrismaService } from "src/prisma/prisma.service"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; +import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; +import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; import { shift_select } from "src/time-and-attendance/utils/selects.utils"; -import { GetShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto"; -import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; diff --git a/src/time-and-attendance/time-tracker/shifts/shifts.module.ts b/src/time-and-attendance/time-tracker/shifts/shifts.module.ts new file mode 100644 index 0000000..3dc407d --- /dev/null +++ b/src/time-and-attendance/time-tracker/shifts/shifts.module.ts @@ -0,0 +1,14 @@ + +import { BusinessLogicsModule } from 'src/time-and-attendance/domains/business-logics.module'; +import { Module } from '@nestjs/common'; +import { ShiftController } from 'src/time-and-attendance/time-tracker/shifts/controllers/shift.controller'; +import { ShiftsGetService } from 'src/time-and-attendance/time-tracker/shifts/services/shifts-get.service'; +import { ShiftsUpsertService } from 'src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service'; + +@Module({ + imports: [ BusinessLogicsModule ], + controllers: [ShiftController], + providers: [ ShiftsGetService, ShiftsUpsertService ], + exports: [ ShiftsUpsertService ], +}) +export class ShiftsModule {} diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts similarity index 90% rename from src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts rename to src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts index 414930a..90cf522 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts @@ -1,5 +1,5 @@ import { Controller, Get, ParseIntPipe, Query, Req, UnauthorizedException} from "@nestjs/common"; -import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service"; +import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service"; @Controller('timesheets') export class TimesheetController { diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/dtos/timesheet.dto.ts b/src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/dtos/timesheet.dto.ts rename to src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-approval.service.ts b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-approval.service.ts rename to src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service.ts b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service.ts rename to src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts similarity index 99% rename from src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts rename to src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts index c6da872..6ea81e2 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -3,7 +3,7 @@ import { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/time-and-attendance/utils/co import { Injectable, NotFoundException } from "@nestjs/common"; import { TotalExpenses, TotalHours } from "src/time-and-attendance/utils/type.utils"; import { PrismaService } from "src/prisma/prisma.service"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; @Injectable() export class GetTimesheetsOverviewService { diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts similarity index 50% rename from src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts rename to src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts index 595d660..f559f6d 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts +++ b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts @@ -1,8 +1,9 @@ -import { GetTimesheetsOverviewService } from 'src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service'; -import { TimesheetArchiveService } from 'src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service'; -import { TimesheetController } from 'src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller'; -import { SharedModule } from 'src/time-and-attendance/modules/shared/shared.module'; + import { Module } from '@nestjs/common'; +import { SharedModule } from 'src/time-and-attendance/shared/shared.module'; +import { TimesheetController } from 'src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller'; +import { TimesheetArchiveService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service'; +import { GetTimesheetsOverviewService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service'; @Module({ imports: [SharedModule], diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts index 0264f3f..5e846c1 100644 --- a/src/time-and-attendance/utils/type.utils.ts +++ b/src/time-and-attendance/utils/type.utils.ts @@ -1,12 +1,13 @@ -import { WeekOvertimeSummary } from "src/time-and-attendance/domains/services/overtime.service"; -import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; -import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; -import { GetShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto"; -import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; import { Prisma } from "@prisma/client"; -import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; -import { GetExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-get.dto"; -import { updateExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-update.dto"; +import { WeekOvertimeSummary } from "src/time-and-attendance/domains/services/overtime.service"; +import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto"; +import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto"; +import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; +import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; +import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; +import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; +import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; + export type TotalHours = { regular: number; From c59b50a8291199373e6f788a8d9af71493f7d94d Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 3 Nov 2025 11:19:21 -0500 Subject: [PATCH 31/57] refactor(schedule-presets): modified route and params to use session data --- docs/swagger/swagger-spec.json | 36 +++---------------- .../controller/schedule-presets.controller.ts | 33 +++++++++-------- .../schedule-presets-apply.service.ts | 11 +++--- .../services/schedule-presets-get.service.ts | 9 +++-- .../schedule-presets-upsert.service.ts | 8 +++-- 5 files changed, 39 insertions(+), 58 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index af88b3e..cca65b6 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -446,19 +446,10 @@ ] } }, - "/schedule-presets/create/{employee_id}": { + "/schedule-presets/create": { "post": { "operationId": "SchedulePresetsController_createPreset", - "parameters": [ - { - "name": "employee_id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], + "parameters": [], "requestBody": { "required": true, "content": { @@ -535,19 +526,10 @@ ] } }, - "/schedule-presets/find/{employee_id}": { + "/schedule-presets/find-list": { "get": { "operationId": "SchedulePresetsController_findListById", - "parameters": [ - { - "name": "employee_id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], + "parameters": [], "responses": { "200": { "description": "" @@ -558,18 +540,10 @@ ] } }, - "/schedule-presets/apply-presets/{employee_id}": { + "/schedule-presets/apply-presets": { "post": { "operationId": "SchedulePresetsController_applyPresets", "parameters": [ - { - "name": "employee_id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - }, { "name": "preset", "required": true, diff --git a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index 3a2c692..56fc28a 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch } from "@nestjs/common"; +import { Controller, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch, Req } from "@nestjs/common"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; import { SchedulePresetsUpdateDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto"; import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; @@ -9,18 +9,18 @@ import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-track @Controller('schedule-presets') export class SchedulePresetsController { constructor( - private readonly upsertService: SchedulePresetsUpsertService, + private readonly upsertService: SchedulePresetsUpsertService, + private readonly getService: SchedulePresetsGetService, private readonly applyPresetsService: SchedulePresetsApplyService, - private readonly getService: SchedulePresetsGetService, ){} //used to create a schedule preset - @Post('create/:employee_id') - async createPreset( - @Param('employee_id', ParseIntPipe) employee_id: number, + @Post('create') + async createPreset( @Req() req, @Body() dto: SchedulePresetsDto, ) { - return await this.upsertService.createPreset(employee_id, dto); + const email = req.user?.email; + return await this.upsertService.createPreset(email, dto); } //used to update an already existing schedule preset @@ -42,23 +42,22 @@ export class SchedulePresetsController { //used to show the list of available schedule presets - @Get('find/:employee_id') - async findListById( - @Param('employee_id', ParseIntPipe) employee_id: number, - ) { - return this.getService.getSchedulePresets(employee_id); + @Get('find-list') + async findListById( @Req() req) { + const email = req.user?.email; + return this.getService.getSchedulePresets(email); } //used to apply a preset to a timesheet - @Post('/apply-presets/:employee_id') - async applyPresets( - @Param('employee_id') employee_id: number, + @Post('apply-presets') + async applyPresets( @Req() req, @Query('preset') preset_name: string, - @Query('start') start_date: string, + @Query('start') start_date: string, ) { + const email = req.user?.email; if(!preset_name?.trim()) throw new BadRequestException('Query "preset" is required'); if(!start_date?.trim()) throw new BadRequestException('Query "start" is required YYYY-MM-DD'); - return this.applyPresetsService.applyToTimesheet(employee_id, preset_name, start_date); + return this.applyPresetsService.applyToTimesheet(email, preset_name, start_date); } } \ No newline at end of file diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts index 75bdf1c..9815e10 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts @@ -4,20 +4,19 @@ import { DATE_ISO_FORMAT } from "src/time-and-attendance/utils/constants.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { ApplyResult } from "src/time-and-attendance/utils/type.utils"; import { WEEKDAY } from "src/time-and-attendance/utils/mappers.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; @Injectable() export class SchedulePresetsApplyService { - constructor( private readonly prisma: PrismaService) {} + constructor( private readonly prisma: PrismaService, private readonly emailResolver: EmailToIdResolver) {} - async applyToTimesheet( - employee_id: number, - preset_name: string, - start_date_iso: string, - ): Promise { + async applyToTimesheet( email: string, preset_name: string, start_date_iso: string ): Promise { if(!preset_name?.trim()) throw new BadRequestException('A preset_name is required'); if(!DATE_ISO_FORMAT.test(start_date_iso)) throw new BadRequestException('start_date must be of format :YYYY-MM-DD'); + const employee_id = await this.emailResolver.findIdByEmail(email); + const preset = await this.prisma.schedulePresets.findFirst({ where: { employee_id, name: preset_name }, include: { diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts index e73227d..8b89c80 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts @@ -2,13 +2,18 @@ import { PresetResponse, ShiftResponse } from "src/time-and-attendance/utils/typ import { PrismaService } from "src/prisma/prisma.service"; import { Injectable } from "@nestjs/common"; import { Prisma } from "@prisma/client"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; @Injectable() export class SchedulePresetsGetService { - constructor( private readonly prisma: PrismaService ){} + constructor( + private readonly prisma: PrismaService, + private readonly emailResolver: EmailToIdResolver, + ){} - async getSchedulePresets(employee_id: number): Promise { + async getSchedulePresets(email: string): Promise { try { + const employee_id = await this.emailResolver.findIdByEmail(email); const presets = await this.prisma.schedulePresets.findMany({ where: { employee_id }, orderBy: [{is_default: 'desc' }, { name: 'asc' }], diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts index ec60558..534082e 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts @@ -5,20 +5,24 @@ import { toHHmmFromDate } from "src/time-and-attendance/utils/date-time.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; @Injectable() export class SchedulePresetsUpsertService { constructor( private readonly prisma: PrismaService, private readonly typeResolver : BankCodesResolver, + private readonly emailResolver: EmailToIdResolver, ){} //_________________________________________________________________ // CREATE //_________________________________________________________________ - async createPreset( employee_id: number, dto: SchedulePresetsDto): Promise { + async createPreset( email: string, dto: SchedulePresetsDto): Promise { try { const shifts_data = await this.resolveAndBuildPresetShifts(dto); - if(!shifts_data) throw new BadRequestException(`Employee with id: ${employee_id} or dto not found`); + const employee_id = await this.emailResolver.findIdByEmail(email); + if(!shifts_data) throw new BadRequestException(`Employee with email: ${email} or dto not found`); + await this.prisma.$transaction(async (tx)=> { if(dto.is_default) { await tx.schedulePresets.updateMany({ From f1f765b35051d41e052397dc001a7837b2e4e98c Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 3 Nov 2025 11:47:41 -0500 Subject: [PATCH 32/57] clean(folder): cleaning imports --- docs/swagger/swagger-spec.json | 265 +----------------- src/modules/exports/csv-exports.module.ts | 3 +- .../domains/business-logics.module.ts | 5 +- .../domains/services/holiday.service.ts | 30 +- .../domains/services/mileage.service.ts | 2 +- .../domains/services/overtime.service.ts | 171 +---------- .../domains/services/sick-leave.service.ts | 2 +- .../domains/services/vacation.service.ts | 2 +- .../services/expense-upsert.service.ts | 2 +- .../leave-requests/leave-requests.module.ts | 4 +- .../holiday-leave-requests.service.ts | 6 +- .../services/leave-request.service.ts | 6 +- .../services/sick-leave-requests.service.ts | 6 +- .../vacation-leave-requests.service.ts | 6 +- .../controllers/pay-periods.controller.ts | 43 +-- .../pay-period/dtos/bulk-crew-approval.dto.ts | 2 +- .../pay-period/dtos/bundle-pay-period.dto.ts | 5 - .../dtos/overview-employee-period.dto.ts | 31 -- .../dtos/overview-pay-period.dto.ts | 35 --- .../pay-period/dtos/pay-period.dto.ts | 17 -- .../shared/helpers/date-time.helpers.ts | 34 --- .../shared/interfaces/shifts.interface.ts | 9 - .../shared/selects/expenses.select.ts | 11 - .../shared/selects/pay-periods.select.ts | 4 - .../shared/selects/shifts.select.ts | 12 - .../shared/shared.module.ts | 22 -- .../time-and-attendance.module.ts | 16 +- .../schedule-presets.module.ts | 2 - .../schedule-presets-apply.service.ts | 2 +- .../services/schedule-presets-get.service.ts | 2 +- .../schedule-presets-upsert.service.ts | 4 +- .../shifts/services/shifts-upsert.service.ts | 2 +- .../timesheet-get-overview.service.ts | 2 +- .../timesheets/timesheets.module.ts | 2 - .../utils/constants.utils.ts | 2 + .../utils/date-time.utils.ts | 37 ++- .../utils/resolve-bank-type-id.utils.ts | 0 .../utils/resolve-email-id.utils.ts | 0 .../utils/resolve-full-name.utils.ts | 0 .../utils/resolve-shifts-id.utils.ts | 2 +- .../utils/resolve-timesheet.utils.ts | 0 .../utils/selects.utils.ts | 31 ++ src/time-and-attendance/utils/type.utils.ts | 33 ++- 43 files changed, 176 insertions(+), 696 deletions(-) delete mode 100644 src/time-and-attendance/shared/helpers/date-time.helpers.ts delete mode 100644 src/time-and-attendance/shared/interfaces/shifts.interface.ts delete mode 100644 src/time-and-attendance/shared/selects/expenses.select.ts delete mode 100644 src/time-and-attendance/shared/selects/pay-periods.select.ts delete mode 100644 src/time-and-attendance/shared/selects/shifts.select.ts delete mode 100644 src/time-and-attendance/shared/shared.module.ts rename src/time-and-attendance/{shared => }/utils/resolve-bank-type-id.utils.ts (100%) rename src/time-and-attendance/{shared => }/utils/resolve-email-id.utils.ts (100%) rename src/time-and-attendance/{shared => }/utils/resolve-full-name.utils.ts (100%) rename src/time-and-attendance/{shared => }/utils/resolve-shifts-id.utils.ts (93%) rename src/time-and-attendance/{shared => }/utils/resolve-timesheet.utils.ts (100%) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index cca65b6..8aa7528 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -91,30 +91,20 @@ "parameters": [ { "name": "date", - "required": false, + "required": true, "in": "query", - "description": "Override for resolving the current period", "schema": { - "example": "2025-08-11", "type": "string" } } ], "responses": { "200": { - "description": "Find current and all pay periods", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PayPeriodBundleDto" - } - } - } + "description": "" } }, - "summary": "Return current pay period and the full list", "tags": [ - "pay-periods" + "PayPeriods" ] } }, @@ -133,22 +123,11 @@ ], "responses": { "200": { - "description": "Pay period found for the selected date", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PayPeriodDto" - } - } - } - }, - "404": { - "description": "Pay period not found for the selected date" + "description": "" } }, - "summary": "Resolve a period by a date within it", "tags": [ - "pay-periods" + "PayPeriods" ] } }, @@ -161,7 +140,6 @@ "required": true, "in": "path", "schema": { - "example": 2024, "type": "number" } }, @@ -169,31 +147,18 @@ "name": "periodNumber", "required": true, "in": "path", - "description": "1..26", "schema": { - "example": 1, "type": "number" } } ], "responses": { "200": { - "description": "Pay period found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PayPeriodDto" - } - } - } - }, - "404": { - "description": "Pay period not found" + "description": "" } }, - "summary": "Find pay period by year and period number", "tags": [ - "pay-periods" + "PayPeriods" ] } }, @@ -206,7 +171,6 @@ "required": true, "in": "path", "schema": { - "example": 2024, "type": "number" } }, @@ -214,49 +178,18 @@ "name": "periodNumber", "required": true, "in": "path", - "description": "1..26", "schema": { - "example": 1, "type": "number" } - }, - { - "name": "email", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - }, - { - "name": "includeSubtree", - "required": false, - "in": "query", - "description": "Include indirect reports", - "schema": { - "example": false, - "type": "boolean" - } } ], "responses": { "200": { - "description": "Crew overview", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PayPeriodOverviewDto" - } - } - } - }, - "404": { - "description": "Pay period not found" + "description": "" } }, - "summary": "Supervisor crew overview for a given pay period", "tags": [ - "pay-periods" + "PayPeriods" ] } }, @@ -269,7 +202,6 @@ "required": true, "in": "path", "schema": { - "example": 2024, "type": "number" } }, @@ -277,31 +209,18 @@ "name": "periodNumber", "required": true, "in": "path", - "description": "1..26", "schema": { - "example": 1, "type": "number" } } ], "responses": { "200": { - "description": "Pay period overview found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PayPeriodOverviewDto" - } - } - } - }, - "404": { - "description": "Pay period not found" + "description": "" } }, - "summary": "Detailed view of a pay period by year + number", "tags": [ - "pay-periods" + "PayPeriods" ] } }, @@ -690,168 +609,6 @@ } }, "schemas": { - "PayPeriodDto": { - "type": "object", - "properties": { - "pay_period_no": { - "type": "number", - "example": 1, - "description": "numéro cyclique de la période entre 1 et 26" - }, - "period_start": { - "type": "string", - "example": "2023-12-17", - "format": "date" - }, - "period_end": { - "type": "string", - "example": "2023-12-30", - "format": "date" - }, - "payday": { - "type": "string", - "example": "2023-01-04", - "format": "date" - }, - "pay_year": { - "type": "number", - "example": 2023 - }, - "label": { - "type": "string", - "example": "2023-12-17 → 2023-12-30" - } - }, - "required": [ - "pay_period_no", - "period_start", - "period_end", - "payday", - "pay_year", - "label" - ] - }, - "PayPeriodBundleDto": { - "type": "object", - "properties": { - "current": { - "description": "Current pay period (resolved from date)", - "allOf": [ - { - "$ref": "#/components/schemas/PayPeriodDto" - } - ] - }, - "periods": { - "description": "All pay periods", - "type": "array", - "items": { - "$ref": "#/components/schemas/PayPeriodDto" - } - } - }, - "required": [ - "current", - "periods" - ] - }, - "EmployeePeriodOverviewDto": { - "type": "object", - "properties": { - "employee_name": { - "type": "string", - "example": "Alex Dupont", - "description": "Nom complet de lemployé" - }, - "regular_hours": { - "type": "number", - "example": 40, - "description": "pay-period`s regular hours" - }, - "other_hours": { - "type": "object", - "example": 0, - "description": "pay-period`s other hours" - }, - "expenses": { - "type": "number", - "example": 420.69, - "description": "pay-period`s total expenses ($)" - }, - "mileage": { - "type": "number", - "example": 40, - "description": "pay-period total mileages (km)" - }, - "is_approved": { - "type": "boolean", - "example": true, - "description": "Tous les timesheets de la période sont approuvés pour cet employé" - } - }, - "required": [ - "employee_name", - "regular_hours", - "other_hours", - "expenses", - "mileage", - "is_approved" - ] - }, - "PayPeriodOverviewDto": { - "type": "object", - "properties": { - "pay_period_no": { - "type": "number", - "example": 1, - "description": "Period number (1–26)" - }, - "pay_year": { - "type": "number", - "example": 2023, - "description": "Calendar year of the period" - }, - "period_start": { - "type": "string", - "example": "2023-12-17", - "format": "date", - "description": "Period start date (YYYY-MM-DD)" - }, - "period_end": { - "type": "string", - "example": "2023-12-30", - "format": "date", - "description": "Period end date (YYYY-MM-DD)" - }, - "payday": { - "type": "string", - "example": "2023-12-30", - "format": "date", - "description": "Period pay day(YYYY-MM-DD)" - }, - "label": { - "type": "string", - "example": "2023-12-17 → 2023-12-30", - "description": "Human-readable label" - }, - "employees_overview": { - "description": "Per-employee overview for the period", - "type": "array", - "items": { - "$ref": "#/components/schemas/EmployeePeriodOverviewDto" - } - } - }, - "required": [ - "pay_period_no", - "pay_year", - "period_start", - "period_end", - "payday", - "label", - "employees_overview" - ] - }, "PreferencesDto": { "type": "object", "properties": {} diff --git a/src/modules/exports/csv-exports.module.ts b/src/modules/exports/csv-exports.module.ts index 30a9b8d..92a5a96 100644 --- a/src/modules/exports/csv-exports.module.ts +++ b/src/modules/exports/csv-exports.module.ts @@ -1,10 +1,9 @@ import { Module } from "@nestjs/common"; import { CsvExportController } from "./controllers/csv-exports.controller"; import { CsvExportService } from "./services/csv-exports.service"; -import { SharedModule } from "src/time-and-attendance/shared/shared.module"; @Module({ - providers:[CsvExportService, SharedModule], + providers:[CsvExportService], controllers: [CsvExportController], }) export class CsvExportModule {} diff --git a/src/time-and-attendance/domains/business-logics.module.ts b/src/time-and-attendance/domains/business-logics.module.ts index 91774ec..4dc801e 100644 --- a/src/time-and-attendance/domains/business-logics.module.ts +++ b/src/time-and-attendance/domains/business-logics.module.ts @@ -4,15 +4,18 @@ import { VacationService } from "./services/vacation.service"; import { HolidayService } from "./services/holiday.service"; import { MileageService } from "./services/mileage.service"; import { Module } from "@nestjs/common"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @Module({ + imports:[], providers: [ HolidayService, MileageService, OvertimeService, SickLeaveService, - VacationService + VacationService, + EmailToIdResolver, ], exports: [ HolidayService, diff --git a/src/time-and-attendance/domains/services/holiday.service.ts b/src/time-and-attendance/domains/services/holiday.service.ts index 15bf4d2..5a0c2b7 100644 --- a/src/time-and-attendance/domains/services/holiday.service.ts +++ b/src/time-and-attendance/domains/services/holiday.service.ts @@ -1,9 +1,8 @@ import { Injectable, Logger, NotFoundException } from "@nestjs/common"; import { computeHours, getWeekStart } from "src/common/utils/date-utils"; -import { PrismaService } from "../../../prisma/prisma.service"; - -const WEEK_IN_MS = 7 * 24 * 60 * 60 * 1000; - +import { PrismaService } from "src/prisma/prisma.service"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; +import { MS_PER_WEEK } from "src/time-and-attendance/utils/constants.utils"; /* le calcul est 1/20 des 4 dernières semaines, précédent la semaine incluant le férier. Un maximum de 08h00 est allouable pour le férier @@ -15,28 +14,19 @@ const WEEK_IN_MS = 7 * 24 * 60 * 60 * 1000; export class HolidayService { private readonly logger = new Logger(HolidayService.name); - constructor(private readonly prisma: PrismaService) {} - - //fetch employee_id by email - private async resolveEmployeeByEmail(email: string): Promise { - const employee = await this.prisma.employees.findFirst({ - where: { - user: { email } - }, - select: { id: true }, - }); - if(!employee) throw new NotFoundException(`Employee with email : ${email} not found`); - return employee.id; - } + constructor( + private readonly prisma: PrismaService, + private readonly emailResolver: EmailToIdResolver, + ) {} private async computeHoursPrevious4WeeksByEmail(email: string, holiday_date: Date): Promise { - const employee_id = await this.resolveEmployeeByEmail(email); + const employee_id = await this.emailResolver.findIdByEmail(email); return this.computeHoursPrevious4Weeks(employee_id, holiday_date); } private async computeHoursPrevious4Weeks(employee_id: number, holiday_date: Date): Promise { const holiday_week_start = getWeekStart(holiday_date); - const window_start = new Date(holiday_week_start.getTime() - 4 * WEEK_IN_MS); + const window_start = new Date(holiday_week_start.getTime() - 4 * MS_PER_WEEK); const window_end = new Date(holiday_week_start.getTime() - 1); const valid_codes = ['G1', 'G43', 'G56', 'G104', 'G105', 'G700']; @@ -60,7 +50,7 @@ export class HolidayService { let capped_total = 0; for(let offset = 1; offset <= 4; offset++) { - const week_start = new Date(holiday_week_start.getTime() - offset * WEEK_IN_MS); + const week_start = new Date(holiday_week_start.getTime() - offset * MS_PER_WEEK); const key = week_start.getTime(); const weekly_hours = hours_by_week.get(key) ?? 0; capped_total += Math.min(weekly_hours, 40); diff --git a/src/time-and-attendance/domains/services/mileage.service.ts b/src/time-and-attendance/domains/services/mileage.service.ts index 030ce42..2b589a9 100644 --- a/src/time-and-attendance/domains/services/mileage.service.ts +++ b/src/time-and-attendance/domains/services/mileage.service.ts @@ -1,5 +1,5 @@ import { BadRequestException, Injectable, Logger } from "@nestjs/common"; -import { PrismaService } from '../../../prisma/prisma.service'; +import { PrismaService } from "src/prisma/prisma.service"; @Injectable() export class MileageService { diff --git a/src/time-and-attendance/domains/services/overtime.service.ts b/src/time-and-attendance/domains/services/overtime.service.ts index be8c15c..e43c4d3 100644 --- a/src/time-and-attendance/domains/services/overtime.service.ts +++ b/src/time-and-attendance/domains/services/overtime.service.ts @@ -1,32 +1,15 @@ import { Injectable, Logger } from '@nestjs/common'; -import { PrismaService } from '../../../prisma/prisma.service'; import { getWeekStart, getWeekEnd, computeHours } from 'src/common/utils/date-utils'; -import { Prisma, PrismaClient } from '@prisma/client'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { DAILY_LIMIT_HOURS, WEEKLY_LIMIT_HOURS } from 'src/time-and-attendance/utils/constants.utils'; +import { Tx, WeekOvertimeSummary } from 'src/time-and-attendance/utils/type.utils'; -type Tx = Prisma.TransactionClient | PrismaClient; - -export type WeekOvertimeSummary = { - week_start:string; - week_end: string; - week_total_hours: number; - weekly_overtime: number; - daily_overtime_kept: number; - total_overtime: number; - breakdown: Array<{ - date:string; - day_hours: number; - day_overtime: number; - daily_kept: number; - running_total_before: number; - }>; -}; @Injectable() export class OvertimeService { private logger = new Logger(OvertimeService.name); - private daily_max = 8; // maximum for regular hours per day - private weekly_max = 40; // maximum for regular hours per week + private INCLUDED_TYPES = ['EMERGENCY', 'EVENING','OVERTIME','REGULAR'] as const; // included types for weekly overtime calculation constructor(private prisma: PrismaService) {} @@ -61,7 +44,7 @@ export class OvertimeService { } const week_total_hours = [ ...day_totals.values()].reduce((a,b) => a + b, 0); - const weekly_overtime = Math.max(0, week_total_hours - this.weekly_max); + const weekly_overtime = Math.max(0, week_total_hours - WEEKLY_LIMIT_HOURS); let running = 0; let daily_kept_sum = 0; @@ -69,9 +52,9 @@ export class OvertimeService { for (const key of days) { const day_hours = day_totals.get(key) ?? 0; - const day_overtime = Math.max(0, day_hours - this.daily_max); + const day_overtime = Math.max(0, day_hours - DAILY_LIMIT_HOURS); - const cap_before_40 = Math.max(0, this.weekly_max - running); + const cap_before_40 = Math.max(0, WEEKLY_LIMIT_HOURS - running); const daily_kept = Math.min(day_overtime, cap_before_40); breakdown.push({ @@ -104,144 +87,4 @@ export class OvertimeService { breakdown, }; } - - // //calculate daily overtime - // async getDailyOvertimeHours(timesheet_id: number, date: Date): Promise { - // const shifts = await this.prisma.shifts.findMany({ - // where: { - // timesheet_id, - // date: date, - // bank_code: { type: { in: this.INCLUDED_TYPES as unknown as string[] } }, - // }, - // select: { start_time: true, end_time: true }, - // orderBy: [{ start_time: 'asc' }], - // }); - - // const total = shifts.map((shift)=> - // computeHours(shift.start_time, shift.end_time, 5)). - // reduce((sum, hours)=> sum + hours, 0); - - // const overtime = Math.max(0, total - this.daily_max); - - // this.logger.debug(`[OVERTIME]-[DAILY] total=${total.toFixed(2)}h, overtime= ${overtime.toFixed(2)}h`); - // return overtime; - // } - - // //calculate Weekly overtime - // async getWeeklyOvertimeHours(timesheet_id: number, ref_date: Date): Promise { - // const week_start = getWeekStart(ref_date); - // const week_end = getWeekEnd(week_start); - - // //fetches all shifts from INCLUDED_TYPES array - // const included_shifts = await this.prisma.shifts.findMany({ - // where: { - // timesheet_id, - // date: { gte:week_start, lte: week_end }, - // bank_code: { type: { in: this.INCLUDED_TYPES as unknown as string[] } }, - // }, - // select: { start_time: true, end_time: true }, - // orderBy: [{date: 'asc'}, {start_time:'asc'}], - // }); - - // //calculate total hours of those shifts minus weekly Max to find total overtime hours - // const total = included_shifts.map(shift => - // computeHours(shift.start_time, shift.end_time, 5)). - // reduce((sum, hours)=> sum+hours, 0); - - // const overtime = Math.max(0, total - this.weekly_max); - - // this.logger.debug(`[OVERTIME]-[WEEKLY] total=${total.toFixed(2)}h, overtime= ${overtime.toFixed(2)}h`); - // return overtime; - // } - - - // //transform REGULAR shifts to OVERTIME when exceed 40hrs of included_types of shift - // async transformRegularHoursToWeeklyOvertime( - // employee_id: number, - // ref_date: Date, - // tx?: Prisma.TransactionClient, - // ): Promise { - // //ensures the use of the transaction if needed. fallback to this.prisma if no transaction is detected. - // const db = tx ?? this.prisma; - - // //calculate weekly overtime - // const overtime_hours = await this.getWeeklyOvertimeHours(employee_id, ref_date); - // if(overtime_hours <= 0) return; - - // const convert_to_minutes = Math.round(overtime_hours * 60); - - // const [regular, overtime] = await Promise.all([ - // db.bankCodes.findFirst({where: { type: 'REGULAR' }, select: { id: true } }), - // db.bankCodes.findFirst({where: { type: 'OVERTIME'}, select: { id: true } }), - // ]); - // if(!regular || !overtime) return; - - // const week_start = getWeekStart(ref_date); - // const week_end = getWeekEnd(week_start); - - // //gets all regular shifts and order them by desc - // const regular_shifts_desc = await db.shifts.findMany({ - // where: { - // date: { gte:week_start, lte: week_end }, - // timesheet: { employee_id }, - // bank_code_id: regular.id, - // }, - // select: { - // id: true, - // timesheet_id: true, - // date: true, - // start_time: true, - // end_time: true, - // is_remote: true, - // comment: true, - // }, - // orderBy: [{date: 'desc'}, {start_time:'desc'}], - // }); - - // let remaining_minutes = convert_to_minutes; - - // for(const shift of regular_shifts_desc) { - // if(remaining_minutes <= 0) break; - - // const start = shift.start_time; - // const end = shift.end_time; - // const duration_in_minutes = Math.max(0, Math.round((end.getTime() - start.getTime())/60000)); - // if(duration_in_minutes === 0) continue; - - // if(duration_in_minutes <= remaining_minutes) { - // await db.shifts.update({ - // where: { id: shift.id }, - // data: { bank_code_id: overtime.id }, - // }); - // remaining_minutes -= duration_in_minutes; - // continue; - // } - // //sets the start_time of the new overtime shift - // const new_overtime_start = new Date(end.getTime() - remaining_minutes * 60000); - - // //shorten the regular shift - // await db.shifts.update({ - // where: { id: shift.id }, - // data: { end_time: new_overtime_start }, - // }); - - // //creates the new overtime shift to replace the shorten regular shift - // await db.shifts.create({ - // data: { - // timesheet_id: shift.timesheet_id, - // date: shift.date, - // start_time: new_overtime_start, - // end_time: end, - // is_remote: shift.is_remote, - // comment: shift.comment, - // bank_code_id: overtime.id, - // }, - // }); - // remaining_minutes = 0; - // } - // this.logger.debug(`[OVERTIME]-[WEEKLY]-[TRANSFORM] emp=${employee_id} - // week: ${week_start.toISOString().slice(0,10)}..${week_end.toISOString().slice(0,10)} - // converted= ${(convert_to_minutes-remaining_minutes)/60}h`); - // } - } 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 6c00113..60a847e 100644 --- a/src/time-and-attendance/domains/services/sick-leave.service.ts +++ b/src/time-and-attendance/domains/services/sick-leave.service.ts @@ -1,6 +1,6 @@ import { getYearStart, roundToQuarterHour } from "src/common/utils/date-utils"; import { Injectable, Logger } from "@nestjs/common"; -import { PrismaService } from "../../../prisma/prisma.service"; +import { PrismaService } from "src/prisma/prisma.service"; @Injectable() export class SickLeaveService { diff --git a/src/time-and-attendance/domains/services/vacation.service.ts b/src/time-and-attendance/domains/services/vacation.service.ts index 9445149..2e3a214 100644 --- a/src/time-and-attendance/domains/services/vacation.service.ts +++ b/src/time-and-attendance/domains/services/vacation.service.ts @@ -1,5 +1,5 @@ import { Injectable, Logger, NotFoundException } from "@nestjs/common"; -import { PrismaService } from "../../../prisma/prisma.service"; +import { PrismaService } from "src/prisma/prisma.service"; @Injectable() export class VacationService { diff --git a/src/time-and-attendance/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/expenses/services/expense-upsert.service.ts index 86a1c9b..d48ea50 100644 --- a/src/time-and-attendance/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/expenses/services/expense-upsert.service.ts @@ -6,7 +6,7 @@ import { expense_select } from "src/time-and-attendance/utils/selects.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @Injectable() diff --git a/src/time-and-attendance/leave-requests/leave-requests.module.ts b/src/time-and-attendance/leave-requests/leave-requests.module.ts index a822a27..eff4f6d 100644 --- a/src/time-and-attendance/leave-requests/leave-requests.module.ts +++ b/src/time-and-attendance/leave-requests/leave-requests.module.ts @@ -1,15 +1,13 @@ import { LeaveRequestController } from "src/time-and-attendance/leave-requests/controllers/leave-requests.controller"; import { LeaveRequestsService } from "src/time-and-attendance/leave-requests/services/leave-request.service"; import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; -import { SharedModule } from "src/time-and-attendance/shared/shared.module"; import { ShiftsModule } from "src/time-and-attendance/time-tracker/shifts/shifts.module"; import { Module } from "@nestjs/common"; @Module({ imports: [ BusinessLogicsModule, - ShiftsModule, - SharedModule + ShiftsModule, ], controllers: [LeaveRequestController], providers: [LeaveRequestsService], diff --git a/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts index 5991686..ce4807b 100644 --- a/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts @@ -1,15 +1,15 @@ import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto"; import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; -import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; import { LeaveRequestsUtils } from "src/time-and-attendance/leave-requests/utils/leave-request.util"; -import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; import { PrismaService } from "src/prisma/prisma.service"; import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/utils/date-time.utils"; diff --git a/src/time-and-attendance/leave-requests/services/leave-request.service.ts b/src/time-and-attendance/leave-requests/services/leave-request.service.ts index 6390a10..499c2d8 100644 --- a/src/time-and-attendance/leave-requests/services/leave-request.service.ts +++ b/src/time-and-attendance/leave-requests/services/leave-request.service.ts @@ -1,18 +1,18 @@ import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; -import { normalizeDates, toDateOnly, toISODateKey } from "src/time-and-attendance/shared/helpers/date-time.helpers"; import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto"; import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; import { roundToQuarterHour } from "src/common/utils/date-utils"; import { LeaveRequestsUtils } from "src/time-and-attendance/leave-requests/utils/leave-request.util"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; -import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; +import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; import { PrismaService } from "src/prisma/prisma.service"; import { mapRowToView } from "../mappers/leave-requests.mapper"; +import { normalizeDates, toDateOnly, toISODateKey } from "src/time-and-attendance/utils/date-time.utils"; @Injectable() export class LeaveRequestsService { constructor( diff --git a/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts index 629dbff..1cff7a8 100644 --- a/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts @@ -1,15 +1,15 @@ import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto"; import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; -import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers"; import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; import { roundToQuarterHour } from "src/common/utils/date-utils"; -import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; import { PrismaService } from "src/prisma/prisma.service"; import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/utils/date-time.utils"; diff --git a/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts index d6f16a5..2e71c39 100644 --- a/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts @@ -1,15 +1,15 @@ import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto"; import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; -import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; import { roundToQuarterHour } from "src/common/utils/date-utils"; -import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; import { PrismaService } from "src/prisma/prisma.service"; import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/utils/date-time.utils"; @Injectable() diff --git a/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts b/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts index 61026c6..65ed977 100644 --- a/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts +++ b/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts @@ -1,5 +1,4 @@ -import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query } from "@nestjs/common"; -import { ApiNotFoundResponse, ApiOperation, ApiParam, ApiQuery, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Query, Req } from "@nestjs/common"; import { PayPeriodDto } from "../dtos/pay-period.dto"; import { PayPeriodOverviewDto } from "../dtos/overview-pay-period.dto"; import { PayPeriodsQueryService } from "../services/pay-periods-query.service"; @@ -9,7 +8,7 @@ import { Roles as RoleEnum } from '.prisma/client'; import { PayPeriodBundleDto } from "../dtos/bundle-pay-period.dto"; import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto"; -@ApiTags('pay-periods') + @Controller('pay-periods') export class PayPeriodsController { @@ -19,9 +18,6 @@ export class PayPeriodsController { ) {} @Get('current-and-all') - @ApiOperation({summary: 'Return current pay period and the full list'}) - @ApiQuery({name: 'date', required:false, example: '2025-08-11', description:'Override for resolving the current period'}) - @ApiResponse({status: 200, description:'Find current and all pay periods', type: PayPeriodBundleDto}) async getCurrentAndAll(@Query('date') date?: string): Promise { const [current, periods] = await Promise.all([ this.queryService.findCurrent(date), @@ -31,19 +27,11 @@ export class PayPeriodsController { } @Get("date/:date") - @ApiOperation({ summary: "Resolve a period by a date within it" }) - @ApiResponse({ status: 200, description: "Pay period found for the selected date", type: PayPeriodDto }) - @ApiNotFoundResponse({ description: "Pay period not found for the selected date" }) async findByDate(@Param("date") date: string) { return this.queryService.findByDate(date); } @Get(":year/:periodNumber") - @ApiOperation({ summary: "Find pay period by year and period number" }) - @ApiParam({ name: "year", type: Number, example: 2024 }) - @ApiParam({ name: "periodNumber", type: Number, example: 1, description: "1..26" }) - @ApiResponse({ status: 200, description: "Pay period found", type: PayPeriodDto }) - @ApiNotFoundResponse({ description: "Pay period not found" }) async findOneByYear( @Param("year", ParseIntPipe) year: number, @Param("periodNumber", ParseIntPipe) period_no: number, @@ -61,30 +49,19 @@ export class PayPeriodsController { @Get(':year/:periodNumber/:email') //@RolesAllowed(RoleEnum.SUPERVISOR) - @ApiOperation({ summary: 'Supervisor crew overview for a given pay period' }) - @ApiParam({ name: 'year', type: Number, example: 2024 }) - @ApiParam({ name: 'periodNumber', type: Number, example: 1, description: '1..26' }) - @ApiQuery({ name: 'includeSubtree', required: false, type: Boolean, example: false, description: 'Include indirect reports' }) - @ApiResponse({ status: 200, description: 'Crew overview', type: PayPeriodOverviewDto }) - @ApiNotFoundResponse({ description: 'Pay period not found' }) - async getCrewOverview( - @Param('year', ParseIntPipe) year: number, - @Param('periodNumber', ParseIntPipe) period_no: number, - @Param('email') email: string, - @Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false, + async getCrewOverview( @Req() req, + @Param('year', ParseIntPipe) year: number, + @Param('periodNumber', ParseIntPipe) period_no: number, + @Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false, ): Promise { + const email = req.user?.email; return this.queryService.getCrewOverview(year, period_no, email, include_subtree); } @Get('overview/:year/:periodNumber') - @ApiOperation({ summary: 'Detailed view of a pay period by year + number' }) - @ApiParam({ name: 'year', type: Number, example: 2024 }) - @ApiParam({ name: 'periodNumber', type: Number, example: 1, description: '1..26' }) - @ApiResponse({ status: 200, description: 'Pay period overview found', type: PayPeriodOverviewDto }) - @ApiNotFoundResponse({ description: 'Pay period not found' }) - async getOverviewByYear( - @Param('year', ParseIntPipe) year: number, - @Param('periodNumber', ParseIntPipe) period_no: number, + async getOverviewByYear( + @Param('year', ParseIntPipe) year: number, + @Param('periodNumber', ParseIntPipe) period_no: number, ): Promise { return this.queryService.getOverviewByYearPeriod(year, period_no); } diff --git a/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts b/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts index 3762ddb..650927d 100644 --- a/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts +++ b/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts @@ -1,5 +1,5 @@ import { Type } from "class-transformer"; -import { IsArray, IsBoolean, IsEmail, IsInt, IsOptional, ValidateNested } from "class-validator"; +import { IsArray, IsBoolean, IsEmail, IsInt, ValidateNested } from "class-validator"; export class BulkCrewApprovalItemDto { @IsInt() diff --git a/src/time-and-attendance/pay-period/dtos/bundle-pay-period.dto.ts b/src/time-and-attendance/pay-period/dtos/bundle-pay-period.dto.ts index 9c5a61f..ad84088 100644 --- a/src/time-and-attendance/pay-period/dtos/bundle-pay-period.dto.ts +++ b/src/time-and-attendance/pay-period/dtos/bundle-pay-period.dto.ts @@ -1,11 +1,6 @@ -import { ApiProperty } from "@nestjs/swagger"; import { PayPeriodDto } from "./pay-period.dto"; export class PayPeriodBundleDto { - - @ApiProperty({ type: PayPeriodDto, description: 'Current pay period (resolved from date)' }) current: PayPeriodDto; - - @ApiProperty({ type: [PayPeriodDto], description: 'All pay periods' }) periods: PayPeriodDto[]; } \ No newline at end of file diff --git a/src/time-and-attendance/pay-period/dtos/overview-employee-period.dto.ts b/src/time-and-attendance/pay-period/dtos/overview-employee-period.dto.ts index 1ea6937..4cae27b 100644 --- a/src/time-and-attendance/pay-period/dtos/overview-employee-period.dto.ts +++ b/src/time-and-attendance/pay-period/dtos/overview-employee-period.dto.ts @@ -1,27 +1,7 @@ -import { ApiProperty } from '@nestjs/swagger'; - export class EmployeePeriodOverviewDto { - // @ApiProperty({ - // example: 42, - // description: "Employees.id (clé primaire num.)", - // }) - // @Allow() - // @IsOptional() - // employee_id: number; - - email: string; - - @ApiProperty({ - example: 'Alex Dupont', - description: 'Nom complet de lemployé', - }) employee_name: string; - - @ApiProperty({ example: 40, description: 'pay-period`s regular hours' }) regular_hours: number; - - @ApiProperty({ example: 0, description: 'pay-period`s other hours' }) other_hours: { evening_hours: number; @@ -35,20 +15,9 @@ export class EmployeePeriodOverviewDto { vacation_hours: number; }; - total_hours: number; - - @ApiProperty({ example: 420.69, description: 'pay-period`s total expenses ($)' }) expenses: number; - - @ApiProperty({ example: 40, description: 'pay-period total mileages (km)' }) mileage: number; - - @ApiProperty({ - example: true, - description: 'Tous les timesheets de la période sont approuvés pour cet employé', - }) is_approved: boolean; - is_remote: boolean; } diff --git a/src/time-and-attendance/pay-period/dtos/overview-pay-period.dto.ts b/src/time-and-attendance/pay-period/dtos/overview-pay-period.dto.ts index 041fba3..3748eb8 100644 --- a/src/time-and-attendance/pay-period/dtos/overview-pay-period.dto.ts +++ b/src/time-and-attendance/pay-period/dtos/overview-pay-period.dto.ts @@ -1,46 +1,11 @@ -import { ApiProperty } from '@nestjs/swagger'; import { EmployeePeriodOverviewDto } from './overview-employee-period.dto'; export class PayPeriodOverviewDto { - @ApiProperty({ example: 1, description: 'Period number (1–26)' }) pay_period_no: number; - - @ApiProperty({ example: 2023, description: 'Calendar year of the period' }) pay_year: number; - - @ApiProperty({ - example: '2023-12-17', - type: String, - format: 'date', - description: "Period start date (YYYY-MM-DD)", - }) period_start: string; - - @ApiProperty({ - example: '2023-12-30', - type: String, - format: 'date', - description: "Period end date (YYYY-MM-DD)", - }) period_end: string; - - @ApiProperty({ - example: '2023-12-30', - type: String, - format: 'date', - description: "Period pay day(YYYY-MM-DD)", - }) payday: string; - - @ApiProperty({ - example: '2023-12-17 → 2023-12-30', - description: 'Human-readable label', - }) label: string; - - @ApiProperty({ - type: [EmployeePeriodOverviewDto], - description: 'Per-employee overview for the period', - }) employees_overview: EmployeePeriodOverviewDto[]; } diff --git a/src/time-and-attendance/pay-period/dtos/pay-period.dto.ts b/src/time-and-attendance/pay-period/dtos/pay-period.dto.ts index 4f7989b..a85f481 100644 --- a/src/time-and-attendance/pay-period/dtos/pay-period.dto.ts +++ b/src/time-and-attendance/pay-period/dtos/pay-period.dto.ts @@ -1,25 +1,8 @@ -import { ApiProperty } from "@nestjs/swagger"; - export class PayPeriodDto { - @ApiProperty({ example: 1, - description: 'numéro cyclique de la période entre 1 et 26' }) pay_period_no: number; - - @ApiProperty({ example: '2023-12-17', - type: String, format: 'date' }) period_start: string; - - @ApiProperty({ example: '2023-12-30', - type: String, format: 'date' }) period_end: string; - - @ApiProperty({ example: '2023-01-04', - type: String, format: 'date' }) payday: string; - - @ApiProperty({ example: 2023 }) pay_year: number; - - @ApiProperty({ example: '2023-12-17 → 2023-12-30' }) label: string; } \ No newline at end of file diff --git a/src/time-and-attendance/shared/helpers/date-time.helpers.ts b/src/time-and-attendance/shared/helpers/date-time.helpers.ts deleted file mode 100644 index 2076530..0000000 --- a/src/time-and-attendance/shared/helpers/date-time.helpers.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { BadRequestException } from "@nestjs/common"; - -export const hhmmFromLocal = (d: Date) => - `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`; - -export const toDateOnly = (s: string): Date => { - if (/^\d{4}-\d{2}-\d{2}$/.test(s)) { - const y = Number(s.slice(0,4)); - const m = Number(s.slice(5,7)) - 1; - const d = Number(s.slice(8,10)); - return new Date(y, m, d, 0, 0, 0, 0); - } - const dt = new Date(s); - if (Number.isNaN(dt.getTime())) throw new BadRequestException(`Invalid date: ${s}`); - return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate(), 0,0,0,0); -}; - -// export const toStringFromDate = (d: Date) => -// `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`; - - -export const toISOtoDateOnly = (iso: string): Date => { - const date = new Date(iso); - if (Number.isNaN(date.getTime())) { - throw new BadRequestException(`Invalid date: ${iso}`); - } - date.setHours(0, 0, 0, 0); - return date; -}; - -export const toISODateKey = (date: Date): string => date.toISOString().slice(0, 10); - -export const normalizeDates = (dates: string[]): string[] => - Array.from(new Set(dates.map((iso) => toISODateKey(toISOtoDateOnly(iso))))); \ No newline at end of file diff --git a/src/time-and-attendance/shared/interfaces/shifts.interface.ts b/src/time-and-attendance/shared/interfaces/shifts.interface.ts deleted file mode 100644 index 40f897e..0000000 --- a/src/time-and-attendance/shared/interfaces/shifts.interface.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface ShiftKey { - timesheet_id: number; - date: Date; - start_time: Date; - end_time: Date; - bank_code_id: number; - is_remote: boolean; - comment?: string | null; -} \ No newline at end of file diff --git a/src/time-and-attendance/shared/selects/expenses.select.ts b/src/time-and-attendance/shared/selects/expenses.select.ts deleted file mode 100644 index 540d98f..0000000 --- a/src/time-and-attendance/shared/selects/expenses.select.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const EXPENSE_SELECT = { - date: true, - amount: true, - mileage: true, - comment: true, - is_approved: true, - supervisor_comment: true, - bank_code: { select: { type: true } }, -} as const; - -export const EXPENSE_ASC_ORDER = { date: 'asc' as const }; \ No newline at end of file diff --git a/src/time-and-attendance/shared/selects/pay-periods.select.ts b/src/time-and-attendance/shared/selects/pay-periods.select.ts deleted file mode 100644 index a76f09b..0000000 --- a/src/time-and-attendance/shared/selects/pay-periods.select.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const PAY_PERIOD_SELECT = { - period_start: true, - period_end: true, -} as const; \ No newline at end of file diff --git a/src/time-and-attendance/shared/selects/shifts.select.ts b/src/time-and-attendance/shared/selects/shifts.select.ts deleted file mode 100644 index 8c738e1..0000000 --- a/src/time-and-attendance/shared/selects/shifts.select.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const SHIFT_SELECT = { - date: true, - start_time: true, - end_time: true, - comment: true, - is_approved: true, - is_remote: true, - bank_code: {select: { type: true } }, -} as const; - -export const SHIFT_ASC_ORDER = [{date: 'asc' as const}, {start_time: 'asc' as const}]; - diff --git a/src/time-and-attendance/shared/shared.module.ts b/src/time-and-attendance/shared/shared.module.ts deleted file mode 100644 index 0e26d7b..0000000 --- a/src/time-and-attendance/shared/shared.module.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { EmployeeTimesheetResolver } from "./utils/resolve-timesheet.utils"; -import { EmailToIdResolver } from "./utils/resolve-email-id.utils"; -import { BankCodesResolver } from "./utils/resolve-bank-type-id.utils"; -import { FullNameResolver } from "./utils/resolve-full-name.utils"; -import { PrismaModule } from "src/prisma/prisma.module"; -import { Module } from "@nestjs/common"; - -@Module({ -imports: [PrismaModule], -providers: [ - FullNameResolver, - EmailToIdResolver, - BankCodesResolver, - EmployeeTimesheetResolver, -], -exports: [ - FullNameResolver, - EmailToIdResolver, - BankCodesResolver, - EmployeeTimesheetResolver, -], -}) export class SharedModule {} \ No newline at end of file diff --git a/src/time-and-attendance/time-and-attendance.module.ts b/src/time-and-attendance/time-and-attendance.module.ts index 89de655..1d052df 100644 --- a/src/time-and-attendance/time-and-attendance.module.ts +++ b/src/time-and-attendance/time-and-attendance.module.ts @@ -4,7 +4,6 @@ import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-l import { ExpenseController } from "src/time-and-attendance/expenses/controllers/expense.controller"; import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service"; import { PayperiodsModule } from "src/time-and-attendance/pay-period/pay-periods.module"; -import { SharedModule } from "src/time-and-attendance/shared/shared.module"; import { SchedulePresetsController } from "src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller"; import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; @@ -14,16 +13,17 @@ import { ShiftsGetService } from "src/time-and-attendance/time-tracker/shifts/se import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service"; import { TimesheetController } from "src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service"; +import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @Module({ imports: [ - BusinessLogicsModule, - PayperiodsModule, - SharedModule, + BusinessLogicsModule, + PayperiodsModule, ], controllers: [ - TimesheetController, - ShiftController, + TimesheetController, + ShiftController, SchedulePresetsController, ExpenseController, ], @@ -35,6 +35,8 @@ import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-track SchedulePresetsUpsertService, SchedulePresetsGetService, SchedulePresetsApplyService, + EmailToIdResolver, + BankCodesResolver, ], exports: [], -}) export class TimeAndAttendanceModule{}; \ No newline at end of file +}) export class TimeAndAttendanceModule { }; \ No newline at end of file diff --git a/src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts b/src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts index b261a2f..b17863a 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts @@ -1,6 +1,5 @@ import { Module } from "@nestjs/common"; -import { SharedModule } from "src/time-and-attendance/shared/shared.module"; import { SchedulePresetsController } from "src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller"; import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; @@ -8,7 +7,6 @@ import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-track @Module({ - imports: [SharedModule], controllers: [SchedulePresetsController], providers: [ SchedulePresetsUpsertService, diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts index 9815e10..c055373 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts @@ -4,7 +4,7 @@ import { DATE_ISO_FORMAT } from "src/time-and-attendance/utils/constants.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { ApplyResult } from "src/time-and-attendance/utils/type.utils"; import { WEEKDAY } from "src/time-and-attendance/utils/mappers.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @Injectable() diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts index 8b89c80..3bb3050 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts @@ -2,7 +2,7 @@ import { PresetResponse, ShiftResponse } from "src/time-and-attendance/utils/typ import { PrismaService } from "src/prisma/prisma.service"; import { Injectable } from "@nestjs/common"; import { Prisma } from "@prisma/client"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @Injectable() export class SchedulePresetsGetService { diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts index 534082e..0eaef09 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts @@ -3,9 +3,9 @@ import { CreatePresetResult, DeletePresetResult, UpdatePresetResult } from "src/ import { Prisma, Weekday } from "@prisma/client"; import { toHHmmFromDate } from "src/time-and-attendance/utils/date-time.utils"; import { PrismaService } from "src/prisma/prisma.service"; -import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; +import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @Injectable() export class SchedulePresetsUpsertService { diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 44261cb..59dc811 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -3,7 +3,7 @@ import { overlaps, toStringFromHHmm, toStringFromDate, toDateFromString, toHHmmF import { Injectable, BadRequestException, ConflictException, NotFoundException } from "@nestjs/common"; import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; import { PrismaService } from "src/prisma/prisma.service"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; diff --git a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts index 6ea81e2..ef7fdd6 100644 --- a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -3,7 +3,7 @@ import { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/time-and-attendance/utils/co import { Injectable, NotFoundException } from "@nestjs/common"; import { TotalExpenses, TotalHours } from "src/time-and-attendance/utils/type.utils"; import { PrismaService } from "src/prisma/prisma.service"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @Injectable() export class GetTimesheetsOverviewService { diff --git a/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts index f559f6d..a12322a 100644 --- a/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts +++ b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts @@ -1,12 +1,10 @@ import { Module } from '@nestjs/common'; -import { SharedModule } from 'src/time-and-attendance/shared/shared.module'; import { TimesheetController } from 'src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller'; import { TimesheetArchiveService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service'; import { GetTimesheetsOverviewService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service'; @Module({ - imports: [SharedModule], controllers: [TimesheetController], providers: [ TimesheetArchiveService, diff --git a/src/time-and-attendance/utils/constants.utils.ts b/src/time-and-attendance/utils/constants.utils.ts index e2f20fc..2a53b28 100644 --- a/src/time-and-attendance/utils/constants.utils.ts +++ b/src/time-and-attendance/utils/constants.utils.ts @@ -6,6 +6,8 @@ export const ANCHOR_ISO = '2023-12-17'; export const PERIOD_DAYS = 14; export const PERIODS_PER_YEAR = 26; export const MS_PER_DAY = 86_400_000; +export const MS_PER_WEEK = 7 * 24 * 60 * 60 * 1000; + //REGEX CONSTANTS export const DATE_ISO_FORMAT = /^\d{4}-\d{2}-\d{2}$/; diff --git a/src/time-and-attendance/utils/date-time.utils.ts b/src/time-and-attendance/utils/date-time.utils.ts index 07ea016..cfba4e8 100644 --- a/src/time-and-attendance/utils/date-time.utils.ts +++ b/src/time-and-attendance/utils/date-time.utils.ts @@ -1,3 +1,4 @@ +import { BadRequestException } from "@nestjs/common"; import { ANCHOR_ISO, MS_PER_DAY, PERIODS_PER_YEAR, PERIOD_DAYS } from "src/time-and-attendance/utils/constants.utils"; //ensures the week starts from sunday @@ -88,4 +89,38 @@ export function listPayYear(pay_year: number, anchorISO = ANCHOR_ISO) { } export const overlaps = (a: { start: Date; end: Date }, b: { start: Date; end: Date }) => - !(a.end <= b.start || a.start >= b.end); \ No newline at end of file + !(a.end <= b.start || a.start >= b.end); + + +export const hhmmFromLocal = (d: Date) => + `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`; + +export const toDateOnly = (s: string): Date => { + if (/^\d{4}-\d{2}-\d{2}$/.test(s)) { + const y = Number(s.slice(0,4)); + const m = Number(s.slice(5,7)) - 1; + const d = Number(s.slice(8,10)); + return new Date(y, m, d, 0, 0, 0, 0); + } + const dt = new Date(s); + if (Number.isNaN(dt.getTime())) throw new BadRequestException(`Invalid date: ${s}`); + return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate(), 0,0,0,0); +}; + +// export const toStringFromDate = (d: Date) => +// `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`; + + +export const toISOtoDateOnly = (iso: string): Date => { + const date = new Date(iso); + if (Number.isNaN(date.getTime())) { + throw new BadRequestException(`Invalid date: ${iso}`); + } + date.setHours(0, 0, 0, 0); + return date; +}; + +export const toISODateKey = (date: Date): string => date.toISOString().slice(0, 10); + +export const normalizeDates = (dates: string[]): string[] => + Array.from(new Set(dates.map((iso) => toISODateKey(toISOtoDateOnly(iso))))); \ No newline at end of file diff --git a/src/time-and-attendance/shared/utils/resolve-bank-type-id.utils.ts b/src/time-and-attendance/utils/resolve-bank-type-id.utils.ts similarity index 100% rename from src/time-and-attendance/shared/utils/resolve-bank-type-id.utils.ts rename to src/time-and-attendance/utils/resolve-bank-type-id.utils.ts diff --git a/src/time-and-attendance/shared/utils/resolve-email-id.utils.ts b/src/time-and-attendance/utils/resolve-email-id.utils.ts similarity index 100% rename from src/time-and-attendance/shared/utils/resolve-email-id.utils.ts rename to src/time-and-attendance/utils/resolve-email-id.utils.ts diff --git a/src/time-and-attendance/shared/utils/resolve-full-name.utils.ts b/src/time-and-attendance/utils/resolve-full-name.utils.ts similarity index 100% rename from src/time-and-attendance/shared/utils/resolve-full-name.utils.ts rename to src/time-and-attendance/utils/resolve-full-name.utils.ts diff --git a/src/time-and-attendance/shared/utils/resolve-shifts-id.utils.ts b/src/time-and-attendance/utils/resolve-shifts-id.utils.ts similarity index 93% rename from src/time-and-attendance/shared/utils/resolve-shifts-id.utils.ts rename to src/time-and-attendance/utils/resolve-shifts-id.utils.ts index 4d9d313..e76d144 100644 --- a/src/time-and-attendance/shared/utils/resolve-shifts-id.utils.ts +++ b/src/time-and-attendance/utils/resolve-shifts-id.utils.ts @@ -1,7 +1,7 @@ import { Prisma, PrismaClient } from "@prisma/client"; import { NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; -import { ShiftKey } from "../interfaces/shifts.interface"; +import { ShiftKey } from "src/time-and-attendance/utils/type.utils"; type Tx = Prisma.TransactionClient | PrismaClient; diff --git a/src/time-and-attendance/shared/utils/resolve-timesheet.utils.ts b/src/time-and-attendance/utils/resolve-timesheet.utils.ts similarity index 100% rename from src/time-and-attendance/shared/utils/resolve-timesheet.utils.ts rename to src/time-and-attendance/utils/resolve-timesheet.utils.ts diff --git a/src/time-and-attendance/utils/selects.utils.ts b/src/time-and-attendance/utils/selects.utils.ts index 3dbf127..fd4a356 100644 --- a/src/time-and-attendance/utils/selects.utils.ts +++ b/src/time-and-attendance/utils/selects.utils.ts @@ -47,3 +47,34 @@ export const leaveRequestsSelect = { } }, } satisfies Prisma.LeaveRequestsSelect; + + +export const EXPENSE_SELECT = { + date: true, + amount: true, + mileage: true, + comment: true, + is_approved: true, + supervisor_comment: true, + bank_code: { select: { type: true } }, +} as const; + +export const EXPENSE_ASC_ORDER = { date: 'asc' as const }; + +export const PAY_PERIOD_SELECT = { + period_start: true, + period_end: true, +} as const; + +export const SHIFT_SELECT = { + date: true, + start_time: true, + end_time: true, + comment: true, + is_approved: true, + is_remote: true, + bank_code: {select: { type: true } }, +} as const; + +export const SHIFT_ASC_ORDER = [{date: 'asc' as const}, {start_time: 'asc' as const}]; + diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts index 5e846c1..412566a 100644 --- a/src/time-and-attendance/utils/type.utils.ts +++ b/src/time-and-attendance/utils/type.utils.ts @@ -1,5 +1,4 @@ -import { Prisma } from "@prisma/client"; -import { WeekOvertimeSummary } from "src/time-and-attendance/domains/services/overtime.service"; +import { Prisma, PrismaClient } from "@prisma/client"; import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto"; import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; @@ -80,4 +79,32 @@ export type ApplyResult = { export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>; -export type UpsertAction = 'create' | 'update' | 'delete'; \ No newline at end of file +export type UpsertAction = 'create' | 'update' | 'delete'; + +export type Tx = Prisma.TransactionClient | PrismaClient; + +export type WeekOvertimeSummary = { + week_start:string; + week_end: string; + week_total_hours: number; + weekly_overtime: number; + daily_overtime_kept: number; + total_overtime: number; + breakdown: Array<{ + date:string; + day_hours: number; + day_overtime: number; + daily_kept: number; + running_total_before: number; + }>; +}; + +export interface ShiftKey { + timesheet_id: number; + date: Date; + start_time: Date; + end_time: Date; + bank_code_id: number; + is_remote: boolean; + comment?: string | null; +} \ No newline at end of file From 7ee87d84098bc1527201ef59f9c58b2c8bd75464 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 3 Nov 2025 12:08:54 -0500 Subject: [PATCH 33/57] feat(timesheets): implement role guards for timesheets --- .../expenses/controllers/expense.controller.ts | 3 +-- .../controllers/leave-requests.controller.ts | 11 +---------- .../timesheets/controllers/timesheet.controller.ts | 4 ++++ 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/time-and-attendance/expenses/controllers/expense.controller.ts b/src/time-and-attendance/expenses/controllers/expense.controller.ts index 34b83e9..5f9a7ff 100644 --- a/src/time-and-attendance/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/expenses/controllers/expense.controller.ts @@ -16,8 +16,7 @@ export class ExpenseController { } @Patch('update') - update( - @Body() body: { update :{ id: number; dto: updateExpenseDto }}): Promise{ + update(@Body() body: { update :{ id: number; dto: updateExpenseDto }}): Promise{ return this.upsert_service.updateExpense(body.update); } diff --git a/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts b/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts index 53c8e47..62a14c4 100644 --- a/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts +++ b/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts @@ -14,16 +14,7 @@ export class LeaveRequestController { async upsertLeaveRequest(@Body() dto: UpsertLeaveRequestDto) { const { action, leave_requests } = await this.leave_service.handle(dto); return { action, leave_requests }; - }q - - //TODO: - /* - @Get('archive') - findAllArchived(){...} - - @Get('archive/:id') - findOneArchived(id){...} - */ + } } diff --git a/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts index 90cf522..220d068 100644 --- a/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts @@ -1,11 +1,15 @@ import { Controller, Get, ParseIntPipe, Query, Req, UnauthorizedException} from "@nestjs/common"; +import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service"; +import { Roles as RoleEnum } from '.prisma/client'; + @Controller('timesheets') export class TimesheetController { constructor( private readonly timesheetOverview: GetTimesheetsOverviewService ){} @Get() + @RolesAllowed(RoleEnum.SUPERVISOR, RoleEnum.HR, RoleEnum.ACCOUNTING, RoleEnum.ADMIN) async getTimesheetByIds( @Req() req, @Query('year', ParseIntPipe) year: number, From 5268737bd1a4bd61b7ef5d8e284f9bae92359e6a Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 3 Nov 2025 12:11:13 -0500 Subject: [PATCH 34/57] feat(expenses): implement role guards --- .../expenses/controllers/expense.controller.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/time-and-attendance/expenses/controllers/expense.controller.ts b/src/time-and-attendance/expenses/controllers/expense.controller.ts index 5f9a7ff..fb81eb2 100644 --- a/src/time-and-attendance/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/expenses/controllers/expense.controller.ts @@ -3,12 +3,15 @@ import { CreateExpenseResult, UpdateExpenseResult } from "src/time-and-attendanc import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service"; import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto"; import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; +import { RolesAllowed } from "src/common/decorators/roles.decorators"; +import { Roles as RoleEnum } from '.prisma/client'; @Controller('expense') export class ExpenseController { constructor( private readonly upsert_service: ExpenseUpsertService ){} @Post('create') + @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) create( @Req() req, @Body() dto: ExpenseDto): Promise{ const email = req.user?.email; if(!email) throw new UnauthorizedException('Unauthorized User'); @@ -16,12 +19,16 @@ export class ExpenseController { } @Patch('update') + @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) update(@Body() body: { update :{ id: number; dto: updateExpenseDto }}): Promise{ return this.upsert_service.updateExpense(body.update); } @Delete('delete/:expense_id') + @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) remove(@Param('expense_id') expense_id: number) { return this.upsert_service.deleteExpense(expense_id); } -} \ No newline at end of file +} + + From bdbec4f68c541d25d63dc24b77f48d9f90f5d44f Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 3 Nov 2025 14:14:09 -0500 Subject: [PATCH 35/57] feat(pay-period): added approval of timesheets, shifts and expenses by bulk. added route to controller --- docs/swagger/swagger-spec.json | 92 +++++++++----- .../services/expense-upsert.service.ts | 3 +- .../controllers/pay-periods.controller.ts | 47 +++---- .../pay-period/dtos/bulk-crew-approval.dto.ts | 3 - .../pay-period/pay-periods.module.ts | 12 +- .../services/pay-periods-command.service.ts | 119 +++++++++--------- .../controller/schedule-presets.controller.ts | 27 ++-- .../shifts/controllers/shift.controller.ts | 13 +- .../controllers/timesheet.controller.ts | 7 +- .../services/timesheet-approval.service.ts | 4 +- .../timesheets/timesheets.module.ts | 5 + 11 files changed, 177 insertions(+), 155 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 8aa7528..4e6ef5a 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -162,7 +162,31 @@ ] } }, - "/pay-periods/{year}/{periodNumber}/{email}": { + "/pay-periods/crew/pay-period-approval": { + "patch": { + "operationId": "PayPeriodsController_bulkApproval", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BulkCrewApprovalDto" + } + } + } + }, + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "PayPeriods" + ] + } + }, + "/pay-periods/crew/{year}/{periodNumber}": { "get": { "operationId": "PayPeriodsController_getCrewOverview", "parameters": [ @@ -224,6 +248,37 @@ ] } }, + "/timesheets": { + "get": { + "operationId": "TimesheetController_getTimesheetByIds", + "parameters": [ + { + "name": "year", + "required": true, + "in": "query", + "schema": { + "type": "number" + } + }, + { + "name": "period_number", + "required": true, + "in": "query", + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Timesheet" + ] + } + }, "/preferences": { "patch": { "operationId": "PreferencesController_updatePreferences", @@ -257,37 +312,6 @@ ] } }, - "/timesheets": { - "get": { - "operationId": "TimesheetController_getTimesheetByIds", - "parameters": [ - { - "name": "year", - "required": true, - "in": "query", - "schema": { - "type": "number" - } - }, - { - "name": "period_number", - "required": true, - "in": "query", - "schema": { - "type": "number" - } - } - ], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "Timesheet" - ] - } - }, "/shift/create": { "post": { "operationId": "ShiftController_createBatch", @@ -609,6 +633,10 @@ } }, "schemas": { + "BulkCrewApprovalDto": { + "type": "object", + "properties": {} + }, "PreferencesDto": { "type": "object", "properties": {} diff --git a/src/time-and-attendance/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/expenses/services/expense-upsert.service.ts index d48ea50..7f16064 100644 --- a/src/time-and-attendance/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/expenses/services/expense-upsert.service.ts @@ -1,7 +1,6 @@ - import { CreateExpenseResult, UpdateExpensePayload, UpdateExpenseResult, DeleteExpenseResult, NormalizedExpense } from "src/time-and-attendance/utils/type.utils"; import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils"; -import { Injectable, NotFoundException, Req } from "@nestjs/common"; +import { Injectable, NotFoundException } from "@nestjs/common"; import { expense_select } from "src/time-and-attendance/utils/selects.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; diff --git a/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts b/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts index 65ed977..1d43791 100644 --- a/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts +++ b/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts @@ -1,10 +1,9 @@ -import { Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Query, Req } from "@nestjs/common"; -import { PayPeriodDto } from "../dtos/pay-period.dto"; +import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query, Req, UnauthorizedException } from "@nestjs/common"; import { PayPeriodOverviewDto } from "../dtos/overview-pay-period.dto"; import { PayPeriodsQueryService } from "../services/pay-periods-query.service"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { Roles as RoleEnum } from '.prisma/client'; -// import { PayPeriodsCommandService } from "../services/pay-periods-command.service"; +import { PayPeriodsCommandService } from "../services/pay-periods-command.service"; import { PayPeriodBundleDto } from "../dtos/bundle-pay-period.dto"; import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto"; @@ -14,8 +13,8 @@ export class PayPeriodsController { constructor( private readonly queryService: PayPeriodsQueryService, - // private readonly commandService: PayPeriodsCommandService, - ) {} + private readonly commandService: PayPeriodsCommandService, + ) { } @Get('current-and-all') async getCurrentAndAll(@Query('date') date?: string): Promise { @@ -39,42 +38,32 @@ export class PayPeriodsController { return this.queryService.findOneByYearPeriod(year, period_no); } - // @Patch("crew/bulk-approval") - // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) - // @ApiOperation({ summary: "Approve all selected timesheets in the period" }) - // @ApiResponse({ status: 200, description: "Pay period approved" }) - // async bulkApproval(@Body() dto: BulkCrewApprovalDto) { - // return this.commandService.bulkApproveCrew(dto); - // } + @Patch("crew/pay-period-approval") + @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) + async bulkApproval(@Req() req, @Body() dto: BulkCrewApprovalDto) { + const email = req.user?.email; + if(!email) throw new UnauthorizedException(`Session infos not found`); + return this.commandService.bulkApproveCrew(email, dto); + } - @Get(':year/:periodNumber/:email') - //@RolesAllowed(RoleEnum.SUPERVISOR) - async getCrewOverview( @Req() req, + @Get('crew/:year/:periodNumber') + @RolesAllowed(RoleEnum.SUPERVISOR) + async getCrewOverview(@Req() req, @Param('year', ParseIntPipe) year: number, @Param('periodNumber', ParseIntPipe) period_no: number, @Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false, ): Promise { const email = req.user?.email; + if(!email) throw new UnauthorizedException(`Session infos not found`); return this.queryService.getCrewOverview(year, period_no, email, include_subtree); } @Get('overview/:year/:periodNumber') - async getOverviewByYear( - @Param('year', ParseIntPipe) year: number, + @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) + async getOverviewByYear( + @Param('year', ParseIntPipe) year: number, @Param('periodNumber', ParseIntPipe) period_no: number, ): Promise { return this.queryService.getOverviewByYearPeriod(year, period_no); } - - - //_____________________________________________________________________________________________ - // Deprecated or unused methods - //_____________________________________________________________________________________________ - - // @Get() - // @ApiOperation({ summary: 'Find all pay period' }) - // @ApiResponse({status: 200,description: 'List of pay period found', type: PayPeriodDto, isArray: true }) - // async findAll(): Promise { - // return this.queryService.findAll(); - // } } diff --git a/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts b/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts index 650927d..4ba5527 100644 --- a/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts +++ b/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts @@ -16,9 +16,6 @@ export class BulkCrewApprovalItemDto { } export class BulkCrewApprovalDto { - @IsEmail() - supervisor_email: string; - @IsBoolean() include_subtree: boolean = false; diff --git a/src/time-and-attendance/pay-period/pay-periods.module.ts b/src/time-and-attendance/pay-period/pay-periods.module.ts index 3670c72..f971084 100644 --- a/src/time-and-attendance/pay-period/pay-periods.module.ts +++ b/src/time-and-attendance/pay-period/pay-periods.module.ts @@ -1,10 +1,20 @@ import { PayPeriodsQueryService } from "./services/pay-periods-query.service"; import { PayPeriodsController } from "./controllers/pay-periods.controller"; import { Module } from "@nestjs/common"; +import { PayPeriodsCommandService } from "src/time-and-attendance/pay-period/services/pay-periods-command.service"; +import { TimesheetsModule } from "src/time-and-attendance/time-tracker/timesheets/timesheets.module"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; +import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service"; @Module({ + imports:[TimesheetsModule], controllers: [PayPeriodsController], - providers: [PayPeriodsQueryService], + providers: [ + PayPeriodsQueryService, + PayPeriodsCommandService, + EmailToIdResolver, + TimesheetApprovalService, + ], }) export class PayperiodsModule {} \ No newline at end of file diff --git a/src/time-and-attendance/pay-period/services/pay-periods-command.service.ts b/src/time-and-attendance/pay-period/services/pay-periods-command.service.ts index 960e1ef..6c48fe2 100644 --- a/src/time-and-attendance/pay-period/services/pay-periods-command.service.ts +++ b/src/time-and-attendance/pay-period/services/pay-periods-command.service.ts @@ -1,71 +1,72 @@ -// import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from "@nestjs/common"; -// import { PrismaService } from "src/prisma/prisma.service"; -// import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto"; -// import { PayPeriodsQueryService } from "./pay-periods-query.service"; -// import { TimesheetApprovalService } from "src/modules/timesheets/services/timesheet-approval.service"; +import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from "@nestjs/common"; +import { PrismaService } from "src/prisma/prisma.service"; +import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto"; +import { PayPeriodsQueryService } from "./pay-periods-query.service"; +import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service"; -// @Injectable() -// export class PayPeriodsCommandService { -// constructor( -// private readonly prisma: PrismaService, -// private readonly timesheets_approval: TimesheetApprovalService, -// private readonly query: PayPeriodsQueryService, -// ) {} +@Injectable() +export class PayPeriodsCommandService { + constructor( + private readonly prisma: PrismaService, + private readonly timesheets_approval: TimesheetApprovalService, + private readonly query: PayPeriodsQueryService, + ) {} -// //function to approve pay-periods according to selected crew members -// async bulkApproveCrew(dto:BulkCrewApprovalDto): Promise<{updated: number}> { -// const { supervisor_email, include_subtree, items } = dto; -// if(!items?.length) throw new BadRequestException('no items to process'); + //function to approve pay-periods according to selected crew members + async bulkApproveCrew(email: string, dto:BulkCrewApprovalDto): Promise<{updated: number}> { + const { include_subtree, items } = dto; + if(!items?.length) throw new BadRequestException('no items to process'); -// //fetch and validate supervisor status -// const supervisor = await this.query.getSupervisor(supervisor_email); -// if(!supervisor) throw new NotFoundException('No employee record linked to current user'); -// if(!supervisor.is_supervisor) throw new ForbiddenException('Employee is not a supervisor'); + //fetch and validate supervisor status + const supervisor = await this.query.getSupervisor(email); + if(!supervisor) throw new NotFoundException('No employee record linked to current user'); + if(!supervisor.is_supervisor) throw new ForbiddenException('Employee is not a supervisor'); -// //fetches emails of crew members linked to supervisor -// const crew_emails = await this.query.resolveCrewEmails(supervisor.id, include_subtree); + //fetches emails of crew members linked to supervisor + const crew_emails = await this.query.resolveCrewEmails(supervisor.id, include_subtree); -// for(const item of items) { -// if(!crew_emails.has(item.employee_email)) { -// throw new ForbiddenException(`Employee ${item.employee_email} not in supervisor crew`); -// } -// } + for(const item of items) { + if(!crew_emails.has(item.employee_email)) { + throw new ForbiddenException(`Employee ${item.employee_email} not in supervisor crew`); + } + } -// const period_cache = new Map(); -// const getPeriod = async (y:number, no: number) => { -// const key = `${y}-${no}`; -// if(!period_cache.has(key)) return period_cache.get(key)!; -// const period = await this.query.getPeriodWindow(y,no); -// if(!period) throw new NotFoundException(`Pay period ${y}-${no} not found`); -// period_cache.set(key, period); -// return period; -// }; + const period_cache = new Map(); + const getPeriod = async (year:number, period_no: number) => { + const key = `${year}-${period_no}`; + if(period_cache.has(key)) return period_cache.get(key)!; -// let updated = 0; + const period = await this.query.getPeriodWindow(year,period_no); + if(!period) throw new NotFoundException(`Pay period ${year}-${period_no} not found`); + period_cache.set(key, period); + return period; + }; -// await this.prisma.$transaction(async (transaction) => { -// for(const item of items) { -// const { period_start, period_end } = await getPeriod(item.pay_year, item.period_no); + let updated = 0; -// const t_sheets = await transaction.timesheets.findMany({ -// where: { -// employee: { user: { email: item.employee_email } }, -// OR: [ -// {shift : { some: { date: { gte: period_start, lte: period_end } } } }, -// {expense: { some: { date: { gte: period_start, lte: period_end } } } }, -// ], -// }, -// select: { id: true }, -// }); + await this.prisma.$transaction(async (transaction) => { + for(const item of items) { + const { period_start, period_end } = await getPeriod(item.pay_year, item.period_no); -// for(const { id } of t_sheets) { -// await this.timesheets_approval.updateApprovalWithTransaction(transaction, id, item.approve); -// updated++; -// } + const t_sheets = await transaction.timesheets.findMany({ + where: { + employee: { user: { email: item.employee_email } }, + OR: [ + {shift : { some: { date: { gte: period_start, lte: period_end } } } }, + {expense: { some: { date: { gte: period_start, lte: period_end } } } }, + ], + }, + select: { id: true }, + }); + + for(const { id } of t_sheets) { + await this.timesheets_approval.cascadeApprovalWithtx(transaction, id, item.approve); + updated++; + } -// } -// }); -// return {updated}; -// } -// } \ No newline at end of file + } + }); + return {updated}; + } +} \ No newline at end of file diff --git a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index 56fc28a..042b8df 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -1,10 +1,11 @@ import { Controller, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch, Req } from "@nestjs/common"; +import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; import { SchedulePresetsUpdateDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto"; import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; - +import { Roles as RoleEnum } from '.prisma/client'; @Controller('schedule-presets') export class SchedulePresetsController { @@ -16,45 +17,39 @@ export class SchedulePresetsController { //used to create a schedule preset @Post('create') - async createPreset( @Req() req, - @Body() dto: SchedulePresetsDto, - ) { + @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) + async createPreset( @Req() req, @Body() dto: SchedulePresetsDto ) { const email = req.user?.email; return await this.upsertService.createPreset(email, dto); } //used to update an already existing schedule preset @Patch('update/:preset_id') - async updatePreset( - @Param('preset_id', ParseIntPipe) preset_id: number, - @Body() dto: SchedulePresetsUpdateDto, - ) { + @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) + async updatePreset( @Param('preset_id', ParseIntPipe) preset_id: number,@Body() dto: SchedulePresetsUpdateDto ) { return await this.upsertService.updatePreset(preset_id, dto); } //used to delete a schedule preset @Delete('delete/:preset_id') - async deletePreset( - @Param('preset_id') preset_id: number, - ) { + @RolesAllowed(RoleEnum.ADMIN) + async deletePreset( @Param('preset_id') preset_id: number ) { return await this.upsertService.deletePreset(preset_id); } //used to show the list of available schedule presets @Get('find-list') + @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) async findListById( @Req() req) { const email = req.user?.email; return this.getService.getSchedulePresets(email); } - //used to apply a preset to a timesheet @Post('apply-presets') - async applyPresets( @Req() req, - @Query('preset') preset_name: string, - @Query('start') start_date: string, - ) { + @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) + async applyPresets( @Req() req, @Query('preset') preset_name: string, @Query('start') start_date: string ) { const email = req.user?.email; if(!preset_name?.trim()) throw new BadRequestException('Query "preset" is required'); if(!start_date?.trim()) throw new BadRequestException('Query "start" is required YYYY-MM-DD'); diff --git a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts index 07395f9..767191b 100644 --- a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts @@ -3,16 +3,16 @@ import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service"; import { CreateShiftResult, UpdateShiftResult } from "src/time-and-attendance/utils/type.utils"; - +import { Roles as RoleEnum } from '.prisma/client'; +import { RolesAllowed } from "src/common/decorators/roles.decorators"; @Controller('shift') export class ShiftController { constructor( private readonly upsert_service: ShiftsUpsertService ){} @Post('create') - createBatch( - @Req() req, - @Body()dtos: ShiftDto[]): Promise { + @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) + createBatch( @Req() req, @Body()dtos: ShiftDto[]): Promise { const email = req.user?.email; const list = Array.isArray(dtos) ? dtos : []; if(list.length === 0) throw new BadRequestException('Body is missing or invalid (create shifts)'); @@ -22,14 +22,15 @@ export class ShiftController { //change Body to receive dtos @Patch('update') - updateBatch( - @Body() dtos: UpdateShiftDto[]): Promise{ + @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) + updateBatch( @Body() dtos: UpdateShiftDto[]): Promise{ const list = Array.isArray(dtos) ? dtos: []; if(list.length === 0) throw new BadRequestException('Body is missing or invalid (update shifts)'); return this.upsert_service.updateShifts(dtos); } @Delete(':shift_id') + @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) remove(@Param('shift_id') shift_id: number ) { return this.upsert_service.deleteShift(shift_id); } diff --git a/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts index 220d068..ffde38b 100644 --- a/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts @@ -11,12 +11,9 @@ export class TimesheetController { @Get() @RolesAllowed(RoleEnum.SUPERVISOR, RoleEnum.HR, RoleEnum.ACCOUNTING, RoleEnum.ADMIN) async getTimesheetByIds( - @Req() req, - @Query('year', ParseIntPipe) year: number, - @Query('period_number', ParseIntPipe) period_number: number, - ) { + @Req() req, @Query('year', ParseIntPipe) year:number, @Query('period_number', ParseIntPipe) period_number: number) { const email = req.user?.email; - if(!email) throw new UnauthorizedException('Unauthorized User'); + if(!email) throw new UnauthorizedException('Unauthorized User');  return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(email, year, period_number); } } diff --git a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts index 088b5b7..84756c2 100644 --- a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts +++ b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts @@ -27,11 +27,11 @@ import { Injectable } from "@nestjs/common"; const timesheet = await this.updateApprovalWithTransaction(transaction, timesheetId, isApproved); await transaction.shifts.updateMany({ where: { timesheet_id: timesheetId }, - data: { is_approved: isApproved }, + data: { is_approved: isApproved }, }); await transaction.expenses.updateManyAndReturn({ where: { timesheet_id: timesheetId }, - data: { is_approved: isApproved }, + data: { is_approved: isApproved }, }); return timesheet; } diff --git a/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts index a12322a..761a78e 100644 --- a/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts +++ b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts @@ -1,14 +1,19 @@ import { Module } from '@nestjs/common'; import { TimesheetController } from 'src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller'; +import { TimesheetApprovalService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service'; import { TimesheetArchiveService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service'; import { GetTimesheetsOverviewService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service'; +import { EmailToIdResolver } from 'src/time-and-attendance/utils/resolve-email-id.utils'; @Module({ + controllers: [TimesheetController], providers: [ TimesheetArchiveService, GetTimesheetsOverviewService, + TimesheetApprovalService, + EmailToIdResolver, ], exports: [], }) From 29f131e307598674a04c86d3a39b4787e5ca2ac0 Mon Sep 17 00:00:00 2001 From: Nicolas Drolet Date: Mon, 3 Nov 2025 15:43:10 -0500 Subject: [PATCH 36/57] fix(local): switch values back to local for local testing Will need to figure out a method to automatically detect if backend is running locally or remotely to avoid switching URIs back and forth constantly --- .../controllers/auth.controller.ts | 3 +- src/main.ts | 2 +- .../utils/date-time.utils.ts | 82 +++++++++---------- 3 files changed, 44 insertions(+), 43 deletions(-) diff --git a/src/identity-and-account/authentication/controllers/auth.controller.ts b/src/identity-and-account/authentication/controllers/auth.controller.ts index 0ec1ff2..43c9397 100644 --- a/src/identity-and-account/authentication/controllers/auth.controller.ts +++ b/src/identity-and-account/authentication/controllers/auth.controller.ts @@ -12,7 +12,8 @@ export class AuthController { @Get('/callback') @UseGuards(OIDCLoginGuard) loginCallback(@Req() req: Request, @Res() res: Response) { - res.redirect('http://10.100.251.2:9011/#/login-success'); + // res.redirect('http://10.100.251.2:9011/#/login-success'); + res.redirect('http://localhost:9000/#/login-success'); } @Get('/me') diff --git a/src/main.ts b/src/main.ts index fb35236..7c06222 100644 --- a/src/main.ts +++ b/src/main.ts @@ -46,7 +46,7 @@ async function bootstrap() { // Enable CORS app.enableCors({ - origin: ['http://10.100.251.2:9011', 'http://10.100.251.2:9012', 'http://10.100.251.2:9013'], + origin: ['http://10.100.251.2:9011', 'http://10.100.251.2:9012', 'http://10.100.251.2:9013', 'http://localhost:9000'], credentials: true, }); diff --git a/src/time-and-attendance/utils/date-time.utils.ts b/src/time-and-attendance/utils/date-time.utils.ts index c43337d..463b26a 100644 --- a/src/time-and-attendance/utils/date-time.utils.ts +++ b/src/time-and-attendance/utils/date-time.utils.ts @@ -2,32 +2,32 @@ import { ANCHOR_ISO, MS_PER_DAY, PERIODS_PER_YEAR, PERIOD_DAYS } from "src/time- //ensures the week starts from sunday export function weekStartSunday(date_local: Date): Date { - const start = new Date(Date.UTC(date_local.getFullYear(), date_local.getMonth(), date_local.getDate())); - const dow = start.getDay(); // 0 = dimanche - start.setDate(start.getDate() - dow); - start.setHours(0, 0, 0, 0); - return start; + const start = new Date(Date.UTC(date_local.getFullYear(), date_local.getMonth(), date_local.getDate())); + const dow = start.getDay(); // 0 = dimanche + start.setDate(start.getDate() - dow); + start.setHours(0, 0, 0, 0); + return start; } //converts string to HHmm format export const toStringFromHHmm = (date: Date): string => { - const hh = date.getUTCHours().toString().padStart(2, '0'); - const mm = date.getUTCMinutes().toString().padStart(2, '0'); - return `${hh}:${mm}`; + const hh = date.getUTCHours().toString().padStart(2, '0'); + const mm = date.getUTCMinutes().toString().padStart(2, '0'); + return `${hh}:${mm}`; } //converts string to Date format -export const toStringFromDate = (date: Date) => - date.toISOString().slice(0,10); +export const toStringFromDate = (date: Date) => + date.toISOString().slice(0, 10); //converts HHmm format to string export const toHHmmFromString = (hhmm: string): Date => { - const [hh, mm] = hhmm.split(':').map(Number); - const date = new Date('1970-01-01T00:00:00.000Z'); - date.setUTCHours(hh, mm, 0, 0); - return new Date(date); + const [hh, mm] = hhmm.split(':').map(Number); + const date = new Date('1970-01-01T00:00:00.000Z'); + date.setUTCHours(hh, mm, 0, 0); + return new Date(date); } //converts string to HHmm format @@ -39,52 +39,52 @@ export const toHHmmFromDate = (input: Date | string): string => { } //converts Date format to string -export const toDateFromString = (ymd: string | Date): Date => { - return new Date(`${ymd}T00:00:00:000Z`); +export const toDateFromString = (ymd: string | Date): Date => { + return new Date(ymd); } export const toUTCDateFromString = (iso: string | Date) => { - const d = typeof iso === 'string' ? new Date(iso + 'T00:00:00.000Z') : iso; - return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())); + const d = typeof iso === 'string' ? new Date(iso + 'T00:00:00.000Z') : iso; + return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())); }; export const sevenDaysFrom = (date: Date | string): Date[] => { - return Array.from({length: 7 }, (_,i) => { + return Array.from({ length: 7 }, (_, i) => { const d = new Date(date); - d.setUTCDate(d.getUTCDate() + i ); + d.setUTCDate(d.getUTCDate() + i); return d; }); } export function payYearOfDate(date: string | Date, anchorISO = ANCHOR_ISO): number { - const ANCHOR = toUTCDateFromString(anchorISO); - const d = toUTCDateFromString(date); - const days = Math.floor((+d - +ANCHOR) / MS_PER_DAY); - const cycles = Math.floor(days / (PERIODS_PER_YEAR * PERIOD_DAYS)); - return ANCHOR.getUTCFullYear() + 1 + cycles; + const ANCHOR = toUTCDateFromString(anchorISO); + const d = toUTCDateFromString(date); + const days = Math.floor((+d - +ANCHOR) / MS_PER_DAY); + const cycles = Math.floor(days / (PERIODS_PER_YEAR * PERIOD_DAYS)); + return ANCHOR.getUTCFullYear() + 1 + cycles; } //compute labels for periods export function computePeriod(pay_year: number, period_no: number, anchorISO = ANCHOR_ISO) { - const ANCHOR = toUTCDateFromString(anchorISO); - const cycles = pay_year - (ANCHOR.getUTCFullYear() + 1); - const offsetPeriods = cycles * PERIODS_PER_YEAR + (period_no - 1); - const start = new Date(+ANCHOR + offsetPeriods * PERIOD_DAYS * MS_PER_DAY); - const end = new Date(+start + (PERIOD_DAYS - 1) * MS_PER_DAY); - const pay = new Date(end.getTime() + 6 * MS_PER_DAY); - return { - period_no: period_no, - pay_year: pay_year, - payday: toStringFromDate(pay), - period_start: toStringFromDate(start), - period_end: toStringFromDate(end), - label: `${toStringFromDate(start)}.${toStringFromDate(end)}`, - start, end, - }; + const ANCHOR = toUTCDateFromString(anchorISO); + const cycles = pay_year - (ANCHOR.getUTCFullYear() + 1); + const offsetPeriods = cycles * PERIODS_PER_YEAR + (period_no - 1); + const start = new Date(+ANCHOR + offsetPeriods * PERIOD_DAYS * MS_PER_DAY); + const end = new Date(+start + (PERIOD_DAYS - 1) * MS_PER_DAY); + const pay = new Date(end.getTime() + 6 * MS_PER_DAY); + return { + period_no: period_no, + pay_year: pay_year, + payday: toStringFromDate(pay), + period_start: toStringFromDate(start), + period_end: toStringFromDate(end), + label: `${toStringFromDate(start)}.${toStringFromDate(end)}`, + start, end, + }; } //list of all 26 periods for a full year export function listPayYear(pay_year: number, anchorISO = ANCHOR_ISO) { - return Array.from({ length: PERIODS_PER_YEAR }, (_, i) => computePeriod(pay_year, i + 1, anchorISO)); + return Array.from({ length: PERIODS_PER_YEAR }, (_, i) => computePeriod(pay_year, i + 1, anchorISO)); } export const overlaps = (a: { start: Date; end: Date }, b: { start: Date; end: Date }) => From 6adb614931ac7d36a2e395c43f4da26f01d6864b Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Tue, 4 Nov 2025 08:31:38 -0500 Subject: [PATCH 37/57] refactor(shifts): modified return and switched bank_code_id for types --- docs/swagger/swagger-spec.json | 14 ++++ src/common/shared/base-approval.service.ts | 12 +-- .../holiday-leave-requests.service.ts | 2 +- .../services/leave-request.service.ts | 2 +- .../services/sick-leave-requests.service.ts | 2 +- .../vacation-leave-requests.service.ts | 2 +- .../pay-period/pay-periods.module.ts | 4 +- .../services/pay-periods-command.service.ts | 8 +- .../time-and-attendance.module.ts | 6 +- .../schedule-presets-upsert.service.ts | 2 +- .../shifts/controllers/shift.controller.ts | 6 +- .../shifts/dtos/shift-create.dto.ts | 2 +- .../time-tracker/shifts/dtos/shift-get.dto.ts | 8 +- .../shifts/services/shifts-get.service.ts | 2 +- .../shifts/services/shifts-upsert.service.ts | 52 ++++++------ .../controllers/timesheet.controller.ts | 20 ++++- .../services/timesheet-approval.service.ts | 51 ++++++++---- .../services/timesheet-archive.service.ts | 82 +++++++++---------- .../timesheets/timesheets.module.ts | 7 +- .../utils/resolve-bank-type-id.utils.ts | 23 +++++- .../utils/selects.utils.ts | 12 +++ src/time-and-attendance/utils/type.utils.ts | 2 +- 22 files changed, 203 insertions(+), 118 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 4e6ef5a..4aad9e8 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -279,6 +279,20 @@ ] } }, + "/timesheets/timesheet-approval": { + "patch": { + "operationId": "TimesheetController_approveTimesheet", + "parameters": [], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Timesheet" + ] + } + }, "/preferences": { "patch": { "operationId": "PreferencesController_updatePreferences", diff --git a/src/common/shared/base-approval.service.ts b/src/common/shared/base-approval.service.ts index e3660e1..ab44a0d 100644 --- a/src/common/shared/base-approval.service.ts +++ b/src/common/shared/base-approval.service.ts @@ -18,14 +18,14 @@ export abstract class BaseApprovalService { //returns the corresponding Prisma delegate protected abstract get delegate(): UpdatableDelegate; - protected abstract delegateFor(transaction: Prisma.TransactionClient): UpdatableDelegate; + protected abstract delegateFor(tx: Prisma.TransactionClient): UpdatableDelegate; //standard update Aproval - async updateApproval(id: number, isApproved: boolean): Promise { + async updateApproval(id: number, is_approved: boolean): Promise { try{ return await this.delegate.update({ where: { id }, - data: { is_approved: isApproved }, + data: { is_approved: is_approved }, }); }catch (error: any) { if (error instanceof PrismaClientKnownRequestError && error.code === "P2025") { @@ -36,11 +36,11 @@ export abstract class BaseApprovalService { } //approval with transaction to avoid many requests - async updateApprovalWithTransaction(transaction: Prisma.TransactionClient, id: number, isApproved: boolean): Promise { + async updateApprovalWithTransaction(tx: Prisma.TransactionClient, id: number, is_approved: boolean): Promise { try { - return await this.delegateFor(transaction).update({ + return await this.delegateFor(tx).update({ where: { id }, - data: { is_approved: isApproved }, + data: { is_approved: is_approved }, }); } catch (error: any ){ if(error instanceof PrismaClientKnownRequestError && error.code === 'P2025') { diff --git a/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts index ce4807b..cf18c2e 100644 --- a/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts @@ -26,7 +26,7 @@ export class HolidayLeaveRequestsService { async create(dto: UpsertLeaveRequestDto): Promise { const email = dto.email.trim(); const employee_id = await this.emailResolver.findIdByEmail(email); - const bank_code = await this.typeResolver.findByType(LeaveTypes.HOLIDAY); + const bank_code = await this.typeResolver.findIdAndModifierByType(LeaveTypes.HOLIDAY); const dates = normalizeDates(dto.dates); if (!bank_code) throw new NotFoundException(`bank_code not found`); if (!dates.length) throw new BadRequestException('Dates array must not be empty'); diff --git a/src/time-and-attendance/leave-requests/services/leave-request.service.ts b/src/time-and-attendance/leave-requests/services/leave-request.service.ts index 499c2d8..8b9d7d9 100644 --- a/src/time-and-attendance/leave-requests/services/leave-request.service.ts +++ b/src/time-and-attendance/leave-requests/services/leave-request.service.ts @@ -95,7 +95,7 @@ export class LeaveRequestsService { async update(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise { const email = dto.email.trim(); const employee_id = await this.emailResolver.findIdByEmail(email); - const bank_code = await this.typeResolver.findByType(type); + const bank_code = await this.typeResolver.findIdAndModifierByType(type); if(!bank_code) throw new NotFoundException(`bank_code not found`); const modifier = Number(bank_code.modifier ?? 1); const dates = normalizeDates(dto.dates); diff --git a/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts index 1cff7a8..0b35f88 100644 --- a/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts @@ -25,7 +25,7 @@ export class SickLeaveRequestsService { async create(dto: UpsertLeaveRequestDto): Promise { const email = dto.email.trim(); const employee_id = await this.emailResolver.findIdByEmail(email); - const bank_code = await this.typeResolver.findByType(LeaveTypes.SICK); + const bank_code = await this.typeResolver.findIdAndModifierByType(LeaveTypes.SICK); if(!bank_code) throw new NotFoundException(`bank_code not found`); const modifier = bank_code.modifier ?? 1; diff --git a/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts index 2e71c39..63e0077 100644 --- a/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts @@ -24,7 +24,7 @@ export class VacationLeaveRequestsService { async create(dto: UpsertLeaveRequestDto): Promise { const email = dto.email.trim(); const employee_id = await this.emailResolver.findIdByEmail(email); - const bank_code = await this.typeResolver.findByType(LeaveTypes.VACATION); + const bank_code = await this.typeResolver.findIdAndModifierByType(LeaveTypes.VACATION); if(!bank_code) throw new NotFoundException(`bank_code not found`); const modifier = bank_code.modifier ?? 1; diff --git a/src/time-and-attendance/pay-period/pay-periods.module.ts b/src/time-and-attendance/pay-period/pay-periods.module.ts index f971084..aa49f23 100644 --- a/src/time-and-attendance/pay-period/pay-periods.module.ts +++ b/src/time-and-attendance/pay-period/pay-periods.module.ts @@ -4,7 +4,6 @@ import { Module } from "@nestjs/common"; import { PayPeriodsCommandService } from "src/time-and-attendance/pay-period/services/pay-periods-command.service"; import { TimesheetsModule } from "src/time-and-attendance/time-tracker/timesheets/timesheets.module"; import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; -import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service"; @Module({ imports:[TimesheetsModule], @@ -13,8 +12,7 @@ import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/t PayPeriodsQueryService, PayPeriodsCommandService, EmailToIdResolver, - TimesheetApprovalService, ], }) -export class PayperiodsModule {} \ No newline at end of file +export class PayperiodsModule {} diff --git a/src/time-and-attendance/pay-period/services/pay-periods-command.service.ts b/src/time-and-attendance/pay-period/services/pay-periods-command.service.ts index 6c48fe2..ede4caa 100644 --- a/src/time-and-attendance/pay-period/services/pay-periods-command.service.ts +++ b/src/time-and-attendance/pay-period/services/pay-periods-command.service.ts @@ -8,7 +8,7 @@ import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/t export class PayPeriodsCommandService { constructor( private readonly prisma: PrismaService, - private readonly timesheets_approval: TimesheetApprovalService, + private readonly timesheetsApproval: TimesheetApprovalService, private readonly query: PayPeriodsQueryService, ) {} @@ -49,7 +49,7 @@ export class PayPeriodsCommandService { for(const item of items) { const { period_start, period_end } = await getPeriod(item.pay_year, item.period_no); - const t_sheets = await transaction.timesheets.findMany({ + const timesheets = await transaction.timesheets.findMany({ where: { employee: { user: { email: item.employee_email } }, OR: [ @@ -60,8 +60,8 @@ export class PayPeriodsCommandService { select: { id: true }, }); - for(const { id } of t_sheets) { - await this.timesheets_approval.cascadeApprovalWithtx(transaction, id, item.approve); + for(const { id } of timesheets) { + await this.timesheetsApproval.cascadeApprovalWithtx(transaction, id, item.approve); updated++; } diff --git a/src/time-and-attendance/time-and-attendance.module.ts b/src/time-and-attendance/time-and-attendance.module.ts index 1d052df..5d03b59 100644 --- a/src/time-and-attendance/time-and-attendance.module.ts +++ b/src/time-and-attendance/time-and-attendance.module.ts @@ -12,7 +12,9 @@ import { ShiftController } from "src/time-and-attendance/time-tracker/shifts/con import { ShiftsGetService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-get.service"; import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service"; import { TimesheetController } from "src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller"; +import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service"; +import { TimesheetsModule } from "src/time-and-attendance/time-tracker/timesheets/timesheets.module"; import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @@ -20,6 +22,7 @@ import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-i imports: [ BusinessLogicsModule, PayperiodsModule, + TimesheetsModule, ], controllers: [ TimesheetController, @@ -37,6 +40,7 @@ import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-i SchedulePresetsApplyService, EmailToIdResolver, BankCodesResolver, + TimesheetApprovalService, ], - exports: [], + exports: [TimesheetApprovalService ], }) export class TimeAndAttendanceModule { }; \ No newline at end of file diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts index 0eaef09..cb1938e 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts @@ -171,7 +171,7 @@ export class SchedulePresetsUpsertService { const bank_code_set = new Map(); for (const type of types) { - const { id } = await this.typeResolver.findByType(type); + const { id } = await this.typeResolver.findIdAndModifierByType(type); bank_code_set.set(type, id) } const toTime = (hhmm: string) => new Date(`1970-01-01T${hhmm}:00.000Z`); diff --git a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts index 767191b..29e15fb 100644 --- a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts @@ -5,10 +5,14 @@ import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts import { CreateShiftResult, UpdateShiftResult } from "src/time-and-attendance/utils/type.utils"; import { Roles as RoleEnum } from '.prisma/client'; import { RolesAllowed } from "src/common/decorators/roles.decorators"; +import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; @Controller('shift') export class ShiftController { - constructor( private readonly upsert_service: ShiftsUpsertService ){} + constructor( + private readonly upsert_service: ShiftsUpsertService, + private readonly typeResolver: BankCodesResolver, + ){} @Post('create') @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) diff --git a/src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto.ts b/src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto.ts index 9393b04..c762b0f 100644 --- a/src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto.ts +++ b/src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto.ts @@ -3,7 +3,7 @@ import { IsBoolean, IsInt, IsOptional, IsString, MaxLength } from "class-validat export class ShiftDto { @IsInt() @IsOptional() id: number; @IsInt() timesheet_id!: number; - @IsInt() bank_code_id!: number; + @IsString() type!: string; @IsString() date!: string; @IsString() start_time!: string; diff --git a/src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto.ts b/src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto.ts index 21b479f..969ca98 100644 --- a/src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto.ts +++ b/src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto.ts @@ -1,10 +1,10 @@ export class GetShiftDto { timesheet_id: number; - bank_code_id: number; - date: string; + type: string; + date: string; start_time: string; - end_time: string; - is_remote: boolean; + end_time: string; + is_remote: boolean; is_approved: boolean; comment?: string; } diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts index d1eed96..fb3dc48 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts @@ -44,7 +44,7 @@ export class ShiftsGetService { const shift = row_by_id.get(id)!; return { timesheet_id: shift.timesheet_id, - bank_code_id: shift.bank_code_id, + type: shift.bank_code.type, date: toStringFromDate(shift.date), start_time: toStringFromHHmm(shift.start_time), end_time: toStringFromHHmm(shift.end_time), diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 59dc811..f2c5bd0 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -7,7 +7,8 @@ import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-i import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; -import { shift_select } from "src/time-and-attendance/utils/selects.utils"; +import { shift_select, timesheet_select } from "src/time-and-attendance/utils/selects.utils"; +import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; @@ -17,6 +18,7 @@ export class ShiftsUpsertService { private readonly prisma: PrismaService, private readonly overtime: OvertimeService, private readonly emailResolver: EmailToIdResolver, + private readonly typeResolver: BankCodesResolver, ) { } //_________________________________________________________________ @@ -35,7 +37,7 @@ export class ShiftsUpsertService { const normed_shifts = await Promise.all( dtos.map(async (dto, index) => { try { - const normed = this.normalizeShiftDto(dto); + const normed = await this.normalizeShiftDto(dto); if (normed.end_time <= normed.start_time) { return { index, @@ -45,18 +47,12 @@ export class ShiftsUpsertService { }; } - const start_date = weekStartSunday(normed.date); - - const timesheet = await this.prisma.timesheets.findFirst({ - where: { start_date, employee_id }, - select: { id: true }, + const timesheet = await this.prisma.timesheets.findUnique({ + where: { id: dto.timesheet_id, employee_id }, + select: timesheet_select, }); if (!timesheet) { - return { - index, - error: new NotFoundException(`Timesheet not found`), - }; - + return { index, error: new NotFoundException(`Timesheet not found`)}; } return { @@ -181,7 +177,7 @@ export class ShiftsUpsertService { const row = await tx.shifts.create({ data: { timesheet_id: timesheet_id, - bank_code_id: dto.bank_code_id, + bank_code_id: normed.id, date: normed.date, start_time: normed.start_time, end_time: normed.end_time, @@ -194,10 +190,12 @@ export class ShiftsUpsertService { existing.push({ start_time: row.start_time, end_time: row.end_time }); existing_map.set(map_key, existing); + const {type: bank_type} = await this.typeResolver.findTypeByBankCodeId(row.bank_code_id); const summary = await this.overtime.getWeekOvertimeSummary(timesheet_id, normed.date, tx); + const shift: GetShiftDto = { timesheet_id: timesheet_id, - bank_code_id: row.bank_code_id, + type: bank_type, date: toStringFromDate(row.date), start_time: toStringFromHHmm(row.start_time), end_time: toStringFromHHmm(row.end_time), @@ -227,7 +225,7 @@ export class ShiftsUpsertService { async updateShifts(dtos: UpdateShiftDto[]): Promise { if (!Array.isArray(dtos) || dtos.length === 0) return []; - const updates: UpdateShiftPayload[] = dtos.map((item) => { + const updates: UpdateShiftPayload[] = await Promise.all(dtos.map((item) => { const { id, ...rest } = item; if (!Number.isInteger(id)) { throw new BadRequestException('Update shift payload is missing a valid id'); @@ -237,12 +235,12 @@ export class ShiftsUpsertService { if (rest.date !== undefined) changes.date = rest.date; if (rest.start_time !== undefined) changes.start_time = rest.start_time; if (rest.end_time !== undefined) changes.end_time = rest.end_time; - if (rest.bank_code_id !== undefined) changes.bank_code_id = rest.bank_code_id; + if (rest.type !== undefined) changes.type = rest.type; if (rest.is_remote !== undefined) changes.is_remote = rest.is_remote; if (rest.comment !== undefined) changes.comment = rest.comment; return { id, dto: changes }; - }); + })); return this.prisma.$transaction(async (tx) => { const shift_ids = updates.map(update_shift => update_shift.id); @@ -266,7 +264,7 @@ export class ShiftsUpsertService { } } - const planned_updates = updates.map(update => { + const planned_updates = updates.map( update => { const exist_shift = regroup_id.get(update.id)!; const date_string = update.dto.date ?? toStringFromDate(exist_shift.date); const start_string = update.dto.start_time ?? toStringFromHHmm(exist_shift.start_time); @@ -275,6 +273,7 @@ export class ShiftsUpsertService { date: toDateFromString(date_string), start_time: toHHmmFromString(start_string), end_time: toHHmmFromString(end_string), + id: exist_shift.id, }; return { update, exist_shift, normed }; }); @@ -342,12 +341,12 @@ export class ShiftsUpsertService { for (const planned of planned_updates) { const data: any = {}; const { dto } = planned.update; - if (dto.date !== undefined) data.date = planned.normed.date; + if (dto.date !== undefined) data.date = planned.normed.date; if (dto.start_time !== undefined) data.start_time = planned.normed.start_time; - if (dto.end_time !== undefined) data.end_time = planned.normed.end_time; - if (dto.bank_code_id !== undefined) data.bank_code_id = dto.bank_code_id; - if (dto.is_remote !== undefined) data.is_remote = dto.is_remote; - if (dto.comment !== undefined) data.comment = dto.comment ?? null; + if (dto.end_time !== undefined) data.end_time = planned.normed.end_time; + if (dto.type !== undefined) data.type = dto.type; + if (dto.is_remote !== undefined) data.is_remote = dto.is_remote; + if (dto.comment !== undefined) data.comment = dto.comment ?? null; const row = await tx.shifts.update({ where: { id: planned.exist_shift.id }, @@ -362,7 +361,7 @@ export class ShiftsUpsertService { const shift: GetShiftDto = { timesheet_id: row.timesheet_id, - bank_code_id: row.bank_code_id, + type: data.type, date: toStringFromDate(row.date), start_time: toStringFromHHmm(row.start_time), end_time: toStringFromHHmm(row.end_time), @@ -406,10 +405,11 @@ export class ShiftsUpsertService { // LOCAL HELPERS //_________________________________________________________________ //converts all string hours and date to Date and HHmm formats - private normalizeShiftDto = (dto: ShiftDto): Normalized => { + private normalizeShiftDto = async (dto: ShiftDto): Promise => { + const { id: bank_code_id} = await this.typeResolver.findBankCodeIDByType(dto.type); const date = toDateFromString(dto.date); const start_time = toHHmmFromString(dto.start_time); const end_time = toHHmmFromString(dto.end_time); - return { date, start_time, end_time }; + return { date, start_time, end_time, id: bank_code_id }; } } diff --git a/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts index ffde38b..849a121 100644 --- a/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts @@ -1,19 +1,33 @@ -import { Controller, Get, ParseIntPipe, Query, Req, UnauthorizedException} from "@nestjs/common"; +import { Body, Controller, Get, ParseBoolPipe, ParseIntPipe, Patch, Query, Req, UnauthorizedException} from "@nestjs/common"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service"; import { Roles as RoleEnum } from '.prisma/client'; +import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service"; @Controller('timesheets') export class TimesheetController { - constructor( private readonly timesheetOverview: GetTimesheetsOverviewService ){} + constructor( + private readonly timesheetOverview: GetTimesheetsOverviewService, + private readonly approvalService: TimesheetApprovalService, + ){} @Get() @RolesAllowed(RoleEnum.SUPERVISOR, RoleEnum.HR, RoleEnum.ACCOUNTING, RoleEnum.ADMIN) async getTimesheetByIds( @Req() req, @Query('year', ParseIntPipe) year:number, @Query('period_number', ParseIntPipe) period_number: number) { const email = req.user?.email; - if(!email) throw new UnauthorizedException('Unauthorized User');  + if(!email) throw new UnauthorizedException('Unauthorized User'); return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(email, year, period_number); } + + @Patch('timesheet-approval') + @RolesAllowed(RoleEnum.SUPERVISOR, RoleEnum.HR, RoleEnum.ACCOUNTING, RoleEnum.ADMIN) + async approveTimesheet( + @Body('timesheet_id', ParseIntPipe) timesheet_id: number, + @Body('is_approved' , ParseBoolPipe) is_approved: boolean, + ) { + return this.approvalService.approveTimesheetById(timesheet_id, is_approved); + } } + diff --git a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts index 84756c2..82f9c18 100644 --- a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts +++ b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts @@ -1,11 +1,15 @@ import { BaseApprovalService } from "src/common/shared/base-approval.service"; import { Prisma, Timesheets } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; -import { Injectable } from "@nestjs/common"; +import { Injectable, NotFoundException } from "@nestjs/common"; +import { timesheet_select } from "src/time-and-attendance/utils/selects.utils"; @Injectable() export class TimesheetApprovalService extends BaseApprovalService{ - constructor(prisma: PrismaService){super(prisma)} + constructor( + prisma: PrismaService, + ){super(prisma)} + //_____________________________________________________________________________________________ // APPROVAL AND DELEGATE METHODS //_____________________________________________________________________________________________ @@ -13,26 +17,43 @@ import { Injectable } from "@nestjs/common"; return this.prisma.timesheets; } - protected delegateFor(transaction: Prisma.TransactionClient) { - return transaction.timesheets; + protected delegateFor(tx: Prisma.TransactionClient) { + return tx.timesheets; } - async updateApproval(id: number, isApproved: boolean): Promise { - return this.prisma.$transaction((transaction) => - this.updateApprovalWithTransaction(transaction, id, isApproved), + async updateApproval(id: number, is_approved: boolean): Promise { + return this.prisma.$transaction((tx) => + this.updateApprovalWithTransaction(tx, id, is_approved), ); } - async cascadeApprovalWithtx(transaction: Prisma.TransactionClient, timesheetId: number, isApproved: boolean): Promise { - const timesheet = await this.updateApprovalWithTransaction(transaction, timesheetId, isApproved); - await transaction.shifts.updateMany({ - where: { timesheet_id: timesheetId }, - data: { is_approved: isApproved }, + async cascadeApprovalWithtx(tx: Prisma.TransactionClient, timesheet_id: number, is_approved: boolean): Promise { + const timesheet = await this.updateApprovalWithTransaction(tx, timesheet_id, is_approved); + await tx.shifts.updateMany({ + where: { timesheet_id: timesheet_id }, + data: { is_approved: is_approved }, }); - await transaction.expenses.updateManyAndReturn({ - where: { timesheet_id: timesheetId }, - data: { is_approved: isApproved }, + await tx.expenses.updateManyAndReturn({ + where: { timesheet_id: timesheet_id }, + data: { is_approved: is_approved }, }); return timesheet; } + + async approveTimesheetById( timesheet_id: number, is_approved: boolean){ + return this.prisma.$transaction(async (tx) => { + const timesheet = await tx.timesheets.findUnique({ + where: { id: timesheet_id }, + select: { id: true }, + }); + if(!timesheet) throw new NotFoundException(`Timesheet with id: ${timesheet_id} not found`); + + await this.cascadeApprovalWithtx(tx, timesheet_id, is_approved); + + return tx.timesheets.findUnique({ + where: { id: timesheet_id }, + select: timesheet_select, + }); + }); + } } \ No newline at end of file diff --git a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service.ts b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service.ts index c75bdc3..33fbf76 100644 --- a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service.ts +++ b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service.ts @@ -1,49 +1,49 @@ -import { TimesheetsArchive } from "@prisma/client"; -import { PrismaService } from "src/prisma/prisma.service"; +// import { TimesheetsArchive } from "@prisma/client"; +// import { PrismaService } from "src/prisma/prisma.service"; -export class TimesheetArchiveService { - constructor(private readonly prisma: PrismaService){} +// export class TimesheetArchiveService { +// constructor(private readonly prisma: PrismaService){} - async archiveOld(): Promise { - //calcul du cutoff pour archivation - const cutoff = new Date(); - cutoff.setMonth(cutoff.getMonth() - 6) +// 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: { some: { date: { lt: cutoff } } }, - }, - select: { - id: true, - employee_id: true, - is_approved: true, - }, - }); - if( oldSheets.length === 0) return; +// await this.prisma.$transaction(async transaction => { +// //fetches all timesheets to cutoff +// const oldSheets = await transaction.timesheets.findMany({ +// where: { shift: { some: { 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, - })); +// //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 }); +// //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) } } }); - }); - } +// //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 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 } }); - } -} \ No newline at end of file +// //fetches an archived timesheet +// async findOneArchived(id: number): Promise { +// return this.prisma.timesheetsArchive.findUniqueOrThrow({ where: { id } }); +// } +// } \ No newline at end of file diff --git a/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts index 761a78e..9e96b4d 100644 --- a/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts +++ b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts @@ -2,19 +2,16 @@ import { Module } from '@nestjs/common'; import { TimesheetController } from 'src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller'; import { TimesheetApprovalService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service'; -import { TimesheetArchiveService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service'; import { GetTimesheetsOverviewService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service'; import { EmailToIdResolver } from 'src/time-and-attendance/utils/resolve-email-id.utils'; @Module({ - controllers: [TimesheetController], providers: [ - TimesheetArchiveService, GetTimesheetsOverviewService, - TimesheetApprovalService, EmailToIdResolver, + TimesheetApprovalService, ], - exports: [], + exports: [TimesheetApprovalService], }) export class TimesheetsModule {} diff --git a/src/time-and-attendance/utils/resolve-bank-type-id.utils.ts b/src/time-and-attendance/utils/resolve-bank-type-id.utils.ts index 039543f..017c49f 100644 --- a/src/time-and-attendance/utils/resolve-bank-type-id.utils.ts +++ b/src/time-and-attendance/utils/resolve-bank-type-id.utils.ts @@ -9,7 +9,7 @@ export class BankCodesResolver { constructor(private readonly prisma: PrismaService) {} //find id and modifier by type - readonly findByType = async ( type: string, client?: Tx + readonly findIdAndModifierByType = async ( type: string, client?: Tx ): Promise<{id:number; modifier: number }> => { const db = client ?? this.prisma; const bank = await db.bankCodes.findFirst({ @@ -20,4 +20,25 @@ export class BankCodesResolver { if(!bank) throw new NotFoundException(`Unknown bank code type: ${type}`); return { id: bank.id, modifier: bank.modifier }; }; + + //finds only id by type + readonly findBankCodeIDByType = async (type: string, client?: Tx) => { + const db = client ?? this.prisma; + const bank_code_id = await db.bankCodes.findFirst({ + where: { type }, + select: {id: true}, + }); + if(!bank_code_id) throw new NotFoundException(`Unkown bank type: ${type}`); + return bank_code_id; + } + + readonly findTypeByBankCodeId = async (bank_code_id: number, client?: Tx) => { + const db = client ?? this.prisma; + const type = await db.bankCodes.findFirst({ + where: { id: bank_code_id }, + select: { type: true }, + }); + if(!type) throw new NotFoundException(`Type with id : ${bank_code_id} not found`); + return type; + } } \ No newline at end of file diff --git a/src/time-and-attendance/utils/selects.utils.ts b/src/time-and-attendance/utils/selects.utils.ts index fd4a356..fd8476e 100644 --- a/src/time-and-attendance/utils/selects.utils.ts +++ b/src/time-and-attendance/utils/selects.utils.ts @@ -1,4 +1,5 @@ import { Prisma } from "@prisma/client"; +import { dmmfToRuntimeDataModel } from "@prisma/client/runtime/library"; export const expense_select = { id: true, @@ -17,6 +18,9 @@ export const shift_select = { id: true, timesheet_id: true, bank_code_id: true, + bank_code: { + select: { type: true }, + }, date: true, start_time: true, end_time: true, @@ -78,3 +82,11 @@ export const SHIFT_SELECT = { export const SHIFT_ASC_ORDER = [{date: 'asc' as const}, {start_time: 'asc' as const}]; +export const timesheet_select = { + id: true, + employee_id: true, + shift: true, + expense: true, + start_date: true, + is_approved: true, +} satisfies Prisma.TimesheetsSelect; \ No newline at end of file diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts index 412566a..c9613bb 100644 --- a/src/time-and-attendance/utils/type.utils.ts +++ b/src/time-and-attendance/utils/type.utils.ts @@ -25,7 +25,7 @@ export type TotalExpenses = { mileage: number; }; -export type Normalized = { date: Date; start_time: Date; end_time: Date; }; +export type Normalized = { date: Date; start_time: Date; end_time: Date; id: number}; export type ShiftWithOvertimeDto = { shift: GetShiftDto; From c2203f77cb4d590dc5aa9c1810bfd9e146a93366 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Tue, 4 Nov 2025 09:41:46 -0500 Subject: [PATCH 38/57] fix(shifts): fix overlaping logic --- .../shifts/services/shifts-upsert.service.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index f2c5bd0..52884c7 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -187,8 +187,23 @@ export class ShiftsUpsertService { select: shift_select, }); - existing.push({ start_time: row.start_time, end_time: row.end_time }); - existing_map.set(map_key, existing); + const normalizeHHmm = (value: Date) => toHHmmFromString(toStringFromHHmm(value)); + + for (const { timesheet_id, day, key } of timesheet_keys) { + const day_date = new Date(day); + existing.push({ + start_time: normalizeHHmm(row.start_time), + end_time: normalizeHHmm(row.end_time) + }); + existing_map.set( + key, + existing.map(row => ({ + start_time: normalizeHHmm(row.start_time), + end_time: normalizeHHmm(row.end_time), + })), + ); + } + const {type: bank_type} = await this.typeResolver.findTypeByBankCodeId(row.bank_code_id); const summary = await this.overtime.getWeekOvertimeSummary(timesheet_id, normed.date, tx); From 457757a066de3919b2eabc22f630a85ec4acb1ec Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Tue, 4 Nov 2025 09:55:22 -0500 Subject: [PATCH 39/57] clean(shifts): cleaning --- .../shifts/services/shifts-upsert.service.ts | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 52884c7..46e89b9 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -52,7 +52,7 @@ export class ShiftsUpsertService { select: timesheet_select, }); if (!timesheet) { - return { index, error: new NotFoundException(`Timesheet not found`)}; + return { index, error: new NotFoundException(`Timesheet not found`) }; } return { @@ -136,7 +136,7 @@ export class ShiftsUpsertService { const day_date = new Date(day); const rows = await tx.shifts.findMany({ where: { timesheet_id, date: day_date }, - select: { start_time: true, end_time: true }, + select: { start_time: true, end_time: true, id: true }, }); existing_map.set( key, @@ -153,7 +153,7 @@ export class ShiftsUpsertService { const day_key = new Date(normed.date.getFullYear(), normed.date.getMonth(), normed.date.getDate()).getTime(); const map_key = `${timesheet_id}|${day_key}`; let existing = existing_map.get(map_key); - if(!existing) { + if (!existing) { existing = []; existing_map.set(map_key, existing); } @@ -189,14 +189,13 @@ export class ShiftsUpsertService { const normalizeHHmm = (value: Date) => toHHmmFromString(toStringFromHHmm(value)); - for (const { timesheet_id, day, key } of timesheet_keys) { - const day_date = new Date(day); - existing.push({ - start_time: normalizeHHmm(row.start_time), - end_time: normalizeHHmm(row.end_time) + for (const { key } of timesheet_keys) { + existing.push({ + start_time: normalizeHHmm(row.start_time), + end_time: normalizeHHmm(row.end_time) }); existing_map.set( - key, + key, existing.map(row => ({ start_time: normalizeHHmm(row.start_time), end_time: normalizeHHmm(row.end_time), @@ -204,8 +203,7 @@ export class ShiftsUpsertService { ); } - - const {type: bank_type} = await this.typeResolver.findTypeByBankCodeId(row.bank_code_id); + const { type: bank_type } = await this.typeResolver.findTypeByBankCodeId(row.bank_code_id); const summary = await this.overtime.getWeekOvertimeSummary(timesheet_id, normed.date, tx); const shift: GetShiftDto = { @@ -279,7 +277,7 @@ export class ShiftsUpsertService { } } - const planned_updates = updates.map( update => { + const planned_updates = updates.map(update => { const exist_shift = regroup_id.get(update.id)!; const date_string = update.dto.date ?? toStringFromDate(exist_shift.date); const start_string = update.dto.start_time ?? toStringFromHHmm(exist_shift.start_time); @@ -356,12 +354,12 @@ export class ShiftsUpsertService { for (const planned of planned_updates) { const data: any = {}; const { dto } = planned.update; - if (dto.date !== undefined) data.date = planned.normed.date; + if (dto.date !== undefined) data.date = planned.normed.date; if (dto.start_time !== undefined) data.start_time = planned.normed.start_time; - if (dto.end_time !== undefined) data.end_time = planned.normed.end_time; - if (dto.type !== undefined) data.type = dto.type; - if (dto.is_remote !== undefined) data.is_remote = dto.is_remote; - if (dto.comment !== undefined) data.comment = dto.comment ?? null; + if (dto.end_time !== undefined) data.end_time = planned.normed.end_time; + if (dto.type !== undefined) data.type = dto.type; + if (dto.is_remote !== undefined) data.is_remote = dto.is_remote; + if (dto.comment !== undefined) data.comment = dto.comment ?? null; const row = await tx.shifts.update({ where: { id: planned.exist_shift.id }, @@ -421,7 +419,7 @@ export class ShiftsUpsertService { //_________________________________________________________________ //converts all string hours and date to Date and HHmm formats private normalizeShiftDto = async (dto: ShiftDto): Promise => { - const { id: bank_code_id} = await this.typeResolver.findBankCodeIDByType(dto.type); + const { id: bank_code_id } = await this.typeResolver.findBankCodeIDByType(dto.type); const date = toDateFromString(dto.date); const start_time = toHHmmFromString(dto.start_time); const end_time = toHHmmFromString(dto.end_time); From 0a3d4e2960c5c16616f16c9c71e884b7f4c7f999 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Tue, 4 Nov 2025 14:50:07 -0500 Subject: [PATCH 40/57] fix(shifts): changed date format to UTC --- .../time-tracker/shifts/services/shifts-upsert.service.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 46e89b9..74c441d 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -71,7 +71,7 @@ export class ShiftsUpsertService { const regroup_by_date = new Map(); ok_items.forEach(({ index, normed, timesheet_id }) => { - const day = new Date(normed.date.getFullYear(), normed.date.getMonth(), normed.date.getDate()).getTime(); + const day = new Date(normed.date.getUTCFullYear(), normed.date.getUTCMonth(), normed.date.getUTCDate()).getTime(); const key = `${timesheet_id}|${day}`; if (!regroup_by_date.has(key)) regroup_by_date.set(key, []); regroup_by_date.get(key)!.push(index); @@ -150,7 +150,7 @@ export class ShiftsUpsertService { for (const item of ok_items) { const { index, dto, normed, timesheet_id } = item; - const day_key = new Date(normed.date.getFullYear(), normed.date.getMonth(), normed.date.getDate()).getTime(); + const day_key = new Date(normed.date.getUTCFullYear(), normed.date.getUTCMonth(), normed.date.getUTCDate()).getTime(); const map_key = `${timesheet_id}|${day_key}`; let existing = existing_map.get(map_key); if (!existing) { @@ -293,7 +293,7 @@ export class ShiftsUpsertService { const groups = new Map(); function key(timesheet: number, d: Date) { - const day_date = new Date(d.getFullYear(), d.getMonth(), d.getDate()); + const day_date = new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()); return `${timesheet}|${day_date.getTime()}`; } @@ -304,7 +304,7 @@ export class ShiftsUpsertService { } for (const group of unique_pairs.values()) { - const day_date = new Date(group.date.getFullYear(), group.date.getMonth(), group.date.getDate()); + const day_date = new Date(group.date.getUTCFullYear(), group.date.getUTCMonth(), group.date.getUTCDate()); const existing = await tx.shifts.findMany({ where: { timesheet_id: group.timesheet_id, date: day_date }, select: { id: true, start_time: true, end_time: true }, From eda1f86235b74ad096c13fe096005d1a70613477 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Tue, 4 Nov 2025 15:15:04 -0500 Subject: [PATCH 41/57] refactor(shifts): added date to overlap comparisons --- .../shifts/controllers/shift.controller.ts | 2 - .../shifts/services/shifts-upsert.service.ts | 39 ++++++++++++------- .../utils/date-time.utils.ts | 4 +- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts index 29e15fb..b9ce63d 100644 --- a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts @@ -5,13 +5,11 @@ import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts import { CreateShiftResult, UpdateShiftResult } from "src/time-and-attendance/utils/type.utils"; import { Roles as RoleEnum } from '.prisma/client'; import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; @Controller('shift') export class ShiftController { constructor( private readonly upsert_service: ShiftsUpsertService, - private readonly typeResolver: BankCodesResolver, ){} @Post('create') diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 74c441d..a9212b8 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -93,7 +93,8 @@ export class ShiftsUpsertService { return { index: index, start: item.normed.start_time, - end: item.normed.end_time + end: item.normed.end_time, + date: item.normed.date, }; }) .sort((a, b) => a.start.getTime() - b.start.getTime()); @@ -101,8 +102,8 @@ export class ShiftsUpsertService { for (let j = 1; j < ordered.length; j++) { if ( overlaps( - { start: ordered[j - 1].start, end: ordered[j - 1].end }, - { start: ordered[j].start, end: ordered[j].end } + { start: ordered[j - 1].start, end: ordered[j - 1].end, date: ordered[j - 1].date }, + { start: ordered[j].start, end: ordered[j].end, date: ordered[j].date }, ) ) { const error = new ConflictException({ @@ -130,17 +131,17 @@ export class ShiftsUpsertService { { length: dtos.length }, () => ({ ok: false, error: new Error('uninitialized') })); - const existing_map = new Map(); + const existing_map = new Map(); for (const { timesheet_id, day, key } of timesheet_keys) { const day_date = new Date(day); const rows = await tx.shifts.findMany({ where: { timesheet_id, date: day_date }, - select: { start_time: true, end_time: true, id: true }, + select: { start_time: true, end_time: true, id: true, date: true }, }); existing_map.set( key, - rows.map((row) => ({ start_time: row.start_time, end_time: row.end_time })), + rows.map((row) => ({ start_time: row.start_time, end_time: row.end_time, date: row.date })), ); } @@ -157,7 +158,12 @@ export class ShiftsUpsertService { existing = []; existing_map.set(map_key, existing); } - const hit = existing.find(e => overlaps({ start: e.start_time, end: e.end_time }, { start: normed.start_time, end: normed.end_time })); + const hit = existing.find(exist => overlaps({ + start: exist.start_time, end: exist.end_time, date: exist.date + }, { + start: normed.start_time, end: normed.end_time, date:normed.date + }) + ); if (hit) { results[index] = { ok: false, @@ -167,7 +173,7 @@ export class ShiftsUpsertService { conflicts: [{ start_time: toStringFromHHmm(hit.start_time), end_time: toStringFromHHmm(hit.end_time), - type: 'UNKNOWN', + date: toStringFromDate(hit.date), }], }), }; @@ -192,13 +198,15 @@ export class ShiftsUpsertService { for (const { key } of timesheet_keys) { existing.push({ start_time: normalizeHHmm(row.start_time), - end_time: normalizeHHmm(row.end_time) + end_time: normalizeHHmm(row.end_time), + date: toDateFromString(row.date), }); existing_map.set( key, existing.map(row => ({ start_time: normalizeHHmm(row.start_time), end_time: normalizeHHmm(row.end_time), + date: toDateFromString(row.date), })), ); } @@ -291,7 +299,7 @@ export class ShiftsUpsertService { return { update, exist_shift, normed }; }); - const groups = new Map(); + const groups = new Map(); function key(timesheet: number, d: Date) { const day_date = new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()); return `${timesheet}|${day_date.getTime()}`; @@ -307,9 +315,14 @@ export class ShiftsUpsertService { const day_date = new Date(group.date.getUTCFullYear(), group.date.getUTCMonth(), group.date.getUTCDate()); const existing = await tx.shifts.findMany({ where: { timesheet_id: group.timesheet_id, date: day_date }, - select: { id: true, start_time: true, end_time: true }, + select: { id: true, start_time: true, end_time: true, date: true }, }); - groups.set(key(group.timesheet_id, day_date), { existing: existing.map(row => ({ id: row.id, start: row.start_time, end: row.end_time })), incoming: planned_updates }); + groups.set(key(group.timesheet_id, day_date), { existing: existing.map(row => ({ + id: row.id, + start: row.start_time, + end: row.end_time, + date: row.date, + })), incoming: planned_updates }); } for (const planned of planned_updates) { @@ -317,7 +330,7 @@ export class ShiftsUpsertService { const group = groups.get(keys)!; const conflict = group.existing.find(row => - row.id !== planned.exist_shift.id && overlaps({ start: row.start, end: row.end }, { start: planned.normed.start_time, end: planned.normed.end_time }) + row.id !== planned.exist_shift.id && overlaps({ start: row.start, end: row.end, date: row.date }, { start: planned.normed.start_time, end: planned.normed.end_time }) ); if (conflict) { return updates.map(exist => diff --git a/src/time-and-attendance/utils/date-time.utils.ts b/src/time-and-attendance/utils/date-time.utils.ts index 488bb7a..fda7382 100644 --- a/src/time-and-attendance/utils/date-time.utils.ts +++ b/src/time-and-attendance/utils/date-time.utils.ts @@ -88,8 +88,8 @@ export function listPayYear(pay_year: number, anchorISO = ANCHOR_ISO) { return Array.from({ length: PERIODS_PER_YEAR }, (_, i) => computePeriod(pay_year, i + 1, anchorISO)); } -export const overlaps = (a: { start: Date; end: Date }, b: { start: Date; end: Date }) => - !(a.end <= b.start || a.start >= b.end); +export const overlaps = (a: { start: Date; end: Date, date?: Date; }, b: { start: Date; end: Date; date?: Date; }) => + ((a.date === b.date) && !(a.end <= b.start || a.start >= b.end)); export const hhmmFromLocal = (d: Date) => From 95f369fcbcb5109e76d4f095cc9157f04875ff5a Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Tue, 4 Nov 2025 15:59:03 -0500 Subject: [PATCH 42/57] refactor(shifts): changed to conflictException build for error management for the create function --- .../shifts/services/shifts-upsert.service.ts | 95 +++++++++++-------- 1 file changed, 56 insertions(+), 39 deletions(-) diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index a9212b8..6594cdb 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -39,12 +39,15 @@ export class ShiftsUpsertService { try { const normed = await this.normalizeShiftDto(dto); if (normed.end_time <= normed.start_time) { - return { - index, - error: new BadRequestException( - `end_time must be greater than start_time (index ${index})` - ), - }; + const error = new ConflictException({ + error_code: 'SHIFT_OVERLAP', + conflicts: [{ + start_time: toStringFromHHmm(normed.start_time), + end_time: toStringFromHHmm(normed.end_time), + date: toStringFromDate(normed.date), + }], + }); + return { index, error }; } const timesheet = await this.prisma.timesheets.findUnique({ @@ -52,7 +55,15 @@ export class ShiftsUpsertService { select: timesheet_select, }); if (!timesheet) { - return { index, error: new NotFoundException(`Timesheet not found`) }; + const error = new ConflictException({ + error_code: 'INVALID_TIMESHEET', + conflicts: [{ + start_time: toStringFromHHmm(normed.start_time), + end_time: toStringFromHHmm(normed.end_time), + date: toStringFromDate(normed.date), + }], + }); + return { index, error }; } return { @@ -107,21 +118,17 @@ export class ShiftsUpsertService { ) ) { const error = new ConflictException({ - error_code: 'SHIFT_OVERLAP_BATCH', - message: 'New shift overlaps with another shift in the same batch (same day).', + error_code: 'SHIFT_OVERLAP', + conflicts: [{ + start_time: toStringFromHHmm(ordered[j].start), + end_time: toStringFromHHmm(ordered[j].end), + date: toStringFromDate(ordered[j].date), + }], }); return dtos.map((_dto, key) => indices.includes(key) - ? ({ - ok: false, - error - } as CreateShiftResult) - : ({ - ok: false, - error: new BadRequestException( - 'Batch aborted due to overlaps in another date group' - ), - }), + ? ({ ok: false, error } as CreateShiftResult) + : ({ ok: false, error }), ); } } @@ -139,10 +146,7 @@ export class ShiftsUpsertService { where: { timesheet_id, date: day_date }, select: { start_time: true, end_time: true, id: true, date: true }, }); - existing_map.set( - key, - rows.map((row) => ({ start_time: row.start_time, end_time: row.end_time, date: row.date })), - ); + existing_map.set( key, rows.map((row) => ({ start_time: row.start_time, end_time: row.end_time, date: row.date }))); } normed_shifts.forEach((x, i) => { @@ -158,18 +162,13 @@ export class ShiftsUpsertService { existing = []; existing_map.set(map_key, existing); } - const hit = existing.find(exist => overlaps({ - start: exist.start_time, end: exist.end_time, date: exist.date - }, { - start: normed.start_time, end: normed.end_time, date:normed.date - }) - ); + const hit = existing.find(exist => overlaps({ start: exist.start_time, end: exist.end_time, date: exist.date }, + { start: normed.start_time, end: normed.end_time, date:normed.date})); if (hit) { results[index] = { ok: false, error: new ConflictException({ error_code: 'SHIFT_OVERLAP', - message: 'New shift overlaps with existing shift(s)', conflicts: [{ start_time: toStringFromHHmm(hit.start_time), end_time: toStringFromHHmm(hit.end_time), @@ -248,9 +247,7 @@ export class ShiftsUpsertService { const updates: UpdateShiftPayload[] = await Promise.all(dtos.map((item) => { const { id, ...rest } = item; - if (!Number.isInteger(id)) { - throw new BadRequestException('Update shift payload is missing a valid id'); - } + if (!Number.isInteger(id)) throw new ConflictException({ error_code: 'INVALID_SHIFT'}); const changes: UpdateShiftChanges = {}; if (rest.date !== undefined) changes.date = rest.date; @@ -338,8 +335,11 @@ export class ShiftsUpsertService { ? ({ ok: false, id: exist.id, error: new ConflictException({ error_code: 'SHIFT_OVERLAP', - message: 'New shift overlaps with existing shift(s)', - conflicts: [{ start_time: toStringFromHHmm(conflict.start), end_time: toStringFromHHmm(conflict.end), type: 'UNKNOWN' }], + conflicts: [{ + start_time: toStringFromHHmm(conflict.start), + end_time: toStringFromHHmm(conflict.end), + date: toStringFromDate(conflict.date), + }], }) } as UpdateShiftResult) : ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to overlap in another update') }) @@ -347,17 +347,34 @@ export class ShiftsUpsertService { } } - const regoup_by_day = new Map(); + const regoup_by_day = new Map(); for (const planned of planned_updates) { const keys = key(planned.exist_shift.timesheet_id, planned.normed.date); if (!regoup_by_day.has(keys)) regoup_by_day.set(keys, []); - regoup_by_day.get(keys)!.push({ id: planned.exist_shift.id, start: planned.normed.start_time, end: planned.normed.end_time }); + regoup_by_day.get(keys)!.push({ + id: planned.exist_shift.id, + start: planned.normed.start_time, + end: planned.normed.end_time, + date: planned.normed.date + }); } + for (const arr of regoup_by_day.values()) { arr.sort((a, b) => a.start.getTime() - b.start.getTime()); for (let i = 1; i < arr.length; i++) { - if (overlaps({ start: arr[i - 1].start, end: arr[i - 1].end }, { start: arr[i].start, end: arr[i].end })) { - const error = new ConflictException({ error_code: 'SHIFT_OVERLAP_BATCH', message: 'Overlaps between updates within the same day.' }); + if (overlaps( + { start: arr[i - 1].start, end: arr[i - 1].end, date: arr[i - 1].date }, + { start: arr[i].start, end: arr[i].end, date: arr[i].date }) + ) { + const error = new ConflictException({ + error_code: 'SHIFT_OVERLAP', + conflicts: [{ + start_time: toStringFromHHmm(arr[i].start), + end_time: toStringFromHHmm(arr[i].end), + date: toStringFromDate(arr[i].date), + }], + + }); return updates.map(exist => ({ ok: false, id: exist.id, error: error })); } } From 407f04ac0b741906efd476a80ddc7b6bfcf7762f Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Tue, 4 Nov 2025 16:42:14 -0500 Subject: [PATCH 43/57] fix(shifts): fix a problem with overlaps --- .../shifts/services/shifts-upsert.service.ts | 48 ++++++++----------- .../utils/date-time.utils.ts | 2 +- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 6594cdb..725b12b 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -1,14 +1,14 @@ -import { CreateShiftResult, NormedOk, NormedErr, UpdateShiftResult, UpdateShiftPayload, UpdateShiftChanges, Normalized } from "src/time-and-attendance/utils/type.utils"; -import { overlaps, toStringFromHHmm, toStringFromDate, toDateFromString, toHHmmFromString, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils"; +import { CreateShiftResult, NormedOk, UpdateShiftResult, UpdateShiftPayload, UpdateShiftChanges, Normalized } from "src/time-and-attendance/utils/type.utils"; +import { overlaps, toStringFromHHmm, toStringFromDate, toDateFromString, toHHmmFromString } from "src/time-and-attendance/utils/date-time.utils"; import { Injectable, BadRequestException, ConflictException, NotFoundException } from "@nestjs/common"; -import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; -import { PrismaService } from "src/prisma/prisma.service"; -import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; -import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; -import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; -import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; import { shift_select, timesheet_select } from "src/time-and-attendance/utils/selects.utils"; import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; +import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; +import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; +import { PrismaService } from "src/prisma/prisma.service"; +import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; +import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; @@ -49,6 +49,8 @@ export class ShiftsUpsertService { }); return { index, error }; } + if(!normed.end_time) throw new BadRequestException('A shift needs an end_time'); + if(!normed.start_time) throw new BadRequestException('A shift needs a start_time'); const timesheet = await this.prisma.timesheets.findUnique({ where: { id: dto.timesheet_id, employee_id }, @@ -191,24 +193,15 @@ export class ShiftsUpsertService { }, select: shift_select, }); - const normalizeHHmm = (value: Date) => toHHmmFromString(toStringFromHHmm(value)); - - for (const { key } of timesheet_keys) { - existing.push({ - start_time: normalizeHHmm(row.start_time), - end_time: normalizeHHmm(row.end_time), - date: toDateFromString(row.date), - }); - existing_map.set( - key, - existing.map(row => ({ - start_time: normalizeHHmm(row.start_time), - end_time: normalizeHHmm(row.end_time), - date: toDateFromString(row.date), - })), - ); - } + const normalized_row = { + start_time: normalizeHHmm(row.start_time), + end_time: normalizeHHmm(row.end_time), + date: toDateFromString(row.date), + }; + existing.push(normalized_row); + existing_map.set(map_key, existing); + const { type: bank_type } = await this.typeResolver.findTypeByBankCodeId(row.bank_code_id); const summary = await this.overtime.getWeekOvertimeSummary(timesheet_id, normed.date, tx); @@ -327,7 +320,8 @@ export class ShiftsUpsertService { const group = groups.get(keys)!; const conflict = group.existing.find(row => - row.id !== planned.exist_shift.id && overlaps({ start: row.start, end: row.end, date: row.date }, { start: planned.normed.start_time, end: planned.normed.end_time }) + row.id !== planned.exist_shift.id && overlaps({ start: row.start, end: row.end, date: row.date }, + { start: planned.normed.start_time, end: planned.normed.end_time, date: planned.normed.date }) ); if (conflict) { return updates.map(exist => @@ -432,7 +426,7 @@ export class ShiftsUpsertService { where: { id: shift_id }, select: { id: true, date: true, timesheet_id: true }, }); - if (!shift) throw new NotFoundException(`Shift with id #${shift_id} not found`); + if (!shift) throw new ConflictException({ error_code: 'INVALID_SHIFT'}); await tx.shifts.delete({ where: { id: shift_id } }); diff --git a/src/time-and-attendance/utils/date-time.utils.ts b/src/time-and-attendance/utils/date-time.utils.ts index fda7382..9628970 100644 --- a/src/time-and-attendance/utils/date-time.utils.ts +++ b/src/time-and-attendance/utils/date-time.utils.ts @@ -89,7 +89,7 @@ export function listPayYear(pay_year: number, anchorISO = ANCHOR_ISO) { } export const overlaps = (a: { start: Date; end: Date, date?: Date; }, b: { start: Date; end: Date; date?: Date; }) => - ((a.date === b.date) && !(a.end <= b.start || a.start >= b.end)); + ((a.date?.getTime() === b.date?.getTime()) && !(a.end <= b.start || a.start >= b.end)); export const hhmmFromLocal = (d: Date) => From be007989616c84e75c97852c38bc8f248ddda247 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Wed, 5 Nov 2025 08:26:45 -0500 Subject: [PATCH 44/57] refactor(shifts): change structure of error management returns --- .../shifts/services/shifts-upsert.service.ts | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 725b12b..e1d36f4 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -39,14 +39,14 @@ export class ShiftsUpsertService { try { const normed = await this.normalizeShiftDto(dto); if (normed.end_time <= normed.start_time) { - const error = new ConflictException({ + const error = { error_code: 'SHIFT_OVERLAP', - conflicts: [{ + conflicts: { start_time: toStringFromHHmm(normed.start_time), end_time: toStringFromHHmm(normed.end_time), date: toStringFromDate(normed.date), - }], - }); + }, + }; return { index, error }; } if(!normed.end_time) throw new BadRequestException('A shift needs an end_time'); @@ -57,14 +57,14 @@ export class ShiftsUpsertService { select: timesheet_select, }); if (!timesheet) { - const error = new ConflictException({ + const error = { error_code: 'INVALID_TIMESHEET', - conflicts: [{ + conflicts: { start_time: toStringFromHHmm(normed.start_time), end_time: toStringFromHHmm(normed.end_time), date: toStringFromDate(normed.date), - }], - }); + }, + }; return { index, error }; } @@ -121,11 +121,11 @@ export class ShiftsUpsertService { ) { const error = new ConflictException({ error_code: 'SHIFT_OVERLAP', - conflicts: [{ + conflicts: { start_time: toStringFromHHmm(ordered[j].start), end_time: toStringFromHHmm(ordered[j].end), date: toStringFromDate(ordered[j].date), - }], + }, }); return dtos.map((_dto, key) => indices.includes(key) @@ -169,14 +169,14 @@ export class ShiftsUpsertService { if (hit) { results[index] = { ok: false, - error: new ConflictException({ + error: { error_code: 'SHIFT_OVERLAP', - conflicts: [{ + conflicts: { start_time: toStringFromHHmm(hit.start_time), end_time: toStringFromHHmm(hit.end_time), date: toStringFromDate(hit.date), - }], - }), + }, + }, }; continue; } @@ -327,14 +327,14 @@ export class ShiftsUpsertService { return updates.map(exist => exist.id === planned.exist_shift.id ? ({ - ok: false, id: exist.id, error: new ConflictException({ + ok: false, id: exist.id, error:{ error_code: 'SHIFT_OVERLAP', - conflicts: [{ + conflicts: { start_time: toStringFromHHmm(conflict.start), end_time: toStringFromHHmm(conflict.end), date: toStringFromDate(conflict.date), - }], - }) + }, + } } as UpdateShiftResult) : ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to overlap in another update') }) ); @@ -360,15 +360,15 @@ export class ShiftsUpsertService { { start: arr[i - 1].start, end: arr[i - 1].end, date: arr[i - 1].date }, { start: arr[i].start, end: arr[i].end, date: arr[i].date }) ) { - const error = new ConflictException({ + const error = { error_code: 'SHIFT_OVERLAP', - conflicts: [{ + conflicts: { start_time: toStringFromHHmm(arr[i].start), end_time: toStringFromHHmm(arr[i].end), date: toStringFromDate(arr[i].date), - }], + }, - }); + }; return updates.map(exist => ({ ok: false, id: exist.id, error: error })); } } From 02ebb23d7a006661e584778ef746fbfa540c1a96 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Wed, 5 Nov 2025 08:36:24 -0500 Subject: [PATCH 45/57] refactor(employees): uncomment module and comment archival parts(needs refactoring) --- .../controllers/employees.controller.ts | 168 ++++--- .../employees/dtos/create-employee.dto.ts | 208 ++++----- .../employees/dtos/list-employee.dto.ts | 16 +- .../employees/dtos/profil-employee.dto.ts | 26 +- .../employees/dtos/update-employee.dto.ts | 36 +- .../services/employees-archival.service.ts | 308 ++++++------- .../employees/services/employees.service.ts | 418 +++++++++--------- .../employees/utils/employee.utils.ts | 18 +- 8 files changed, 594 insertions(+), 604 deletions(-) diff --git a/src/identity-and-account/employees/controllers/employees.controller.ts b/src/identity-and-account/employees/controllers/employees.controller.ts index 4828d91..dabca96 100644 --- a/src/identity-and-account/employees/controllers/employees.controller.ts +++ b/src/identity-and-account/employees/controllers/employees.controller.ts @@ -1,99 +1,89 @@ -// import { Body,Controller,Get,NotFoundException,Param,Patch } from '@nestjs/common'; -// import { EmployeesService } from '../services/employees.service'; -// import { CreateEmployeeDto } from '../dtos/create-employee.dto'; -// import { UpdateEmployeeDto } from '../dtos/update-employee.dto'; -// import { RolesAllowed } from '../../../common/decorators/roles.decorators'; -// import { ApiBearerAuth, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger'; -// import { EmployeeListItemDto } from '../dtos/list-employee.dto'; -// import { EmployeesArchivalService } from '../services/employees-archival.service'; -// import { EmployeeProfileItemDto } from 'src/modules/employees/dtos/profil-employee.dto'; +import { Controller, Get, Patch, Param, Body, NotFoundException } from "@nestjs/common"; +import { ApiBearerAuth, ApiOperation, ApiResponse, ApiParam } from "@nestjs/swagger"; +import { EmployeeListItemDto } from "src/identity-and-account/employees/dtos/list-employee.dto"; +import { EmployeeProfileItemDto } from "src/identity-and-account/employees/dtos/profil-employee.dto"; +import { UpdateEmployeeDto } from "src/identity-and-account/employees/dtos/update-employee.dto"; +import { EmployeesArchivalService } from "src/identity-and-account/employees/services/employees-archival.service"; +import { EmployeesService } from "src/identity-and-account/employees/services/employees.service"; -// @ApiTags('Employees') -// @ApiBearerAuth('access-token') -// // @UseGuards() -// @Controller('employees') -// export class EmployeesController { -// constructor( -// private readonly employeesService: EmployeesService, -// private readonly archiveService: EmployeesArchivalService, -// ) {} +@ApiBearerAuth('access-token') +// @UseGuards() +@Controller('employees') +export class EmployeesController { + constructor( + private readonly employeesService: EmployeesService, + private readonly archiveService: EmployeesArchivalService, + ) {} -// @Get('employee-list') -// //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ACCOUNTING) -// @ApiOperation({summary: 'Find all employees with scoped info' }) -// @ApiResponse({ status: 200, description: 'List of employees with scoped info found', type: EmployeeListItemDto, isArray: true }) -// @ApiResponse({ status: 400, description: 'List of employees with scoped info not found' }) -// findListEmployees(): Promise { -// return this.employeesService.findListEmployees(); -// } + @Get('employee-list') + //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ACCOUNTING) -// @Patch(':email') -// //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) -// @ApiBearerAuth('access-token') -// @ApiOperation({ summary: 'Update, archive or restore an employee' }) -// @ApiParam({ name: 'email', type: Number, description: 'Email of the employee' }) -// @ApiResponse({ status: 200, description: 'Employee updated or restored', type: CreateEmployeeDto }) -// @ApiResponse({ status: 202, description: 'Employee archived successfully', type: CreateEmployeeDto }) -// @ApiResponse({ status: 404, description: 'Employee not found in active or archive' }) -// async updateOrArchiveOrRestore(@Param('email') email: string, @Body() dto: UpdateEmployeeDto,) { -// // if last_work_day is set => archive the employee -// // else if employee is archived and first_work_day or last_work_day = null => restore -// //otherwise => standard update -// const result = await this.archiveService.patchEmployee(email, dto); -// if(!result) { -// throw new NotFoundException(`Employee with email: ${ email } is not found in active or archive.`) -// } -// return result; -// } + findListEmployees(): Promise { + return this.employeesService.findListEmployees(); + } -// //_____________________________________________________________________________________________ -// // Deprecated or unused methods -// //_____________________________________________________________________________________________ + @Patch(':email') + //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiBearerAuth('access-token') + async updateOrArchiveOrRestore(@Param('email') email: string, @Body() dto: UpdateEmployeeDto,) { + // if last_work_day is set => archive the employee + // else if employee is archived and first_work_day or last_work_day = null => restore + //otherwise => standard update + const result = await this.archiveService.patchEmployee(email, dto); + if(!result) { + throw new NotFoundException(`Employee with email: ${ email } is not found in active or archive.`) + } + return result; + } -// // @Post() -// // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) -// // @ApiOperation({summary: 'Create employee' }) -// // @ApiResponse({ status: 201, description: 'Employee created', type: CreateEmployeeDto }) -// // @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' }) -// // create(@Body() dto: CreateEmployeeDto): Promise { -// // return this.employeesService.create(dto); -// // } -// // @Get() -// // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ACCOUNTING) -// // @ApiOperation({summary: 'Find all employees' }) -// // @ApiResponse({ status: 200, description: 'List of employees found', type: CreateEmployeeDto, isArray: true }) -// // @ApiResponse({ status: 400, description: 'List of employees not found' }) -// // findAll(): Promise { -// // return this.employeesService.findAll(); -// // } + //_____________________________________________________________________________________________ + // Deprecated or unused methods + //_____________________________________________________________________________________________ + + // @Post() + // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) + // @ApiOperation({summary: 'Create employee' }) + // @ApiResponse({ status: 201, description: 'Employee created', type: CreateEmployeeDto }) + // @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' }) + // create(@Body() dto: CreateEmployeeDto): Promise { + // return this.employeesService.create(dto); + // } + // @Get() + // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ACCOUNTING) + // @ApiOperation({summary: 'Find all employees' }) + // @ApiResponse({ status: 200, description: 'List of employees found', type: CreateEmployeeDto, isArray: true }) + // @ApiResponse({ status: 400, description: 'List of employees not found' }) + // findAll(): Promise { + // return this.employeesService.findAll(); + // } -// // @Get(':email') -// // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR,RoleEnum.ACCOUNTING ) -// // @ApiOperation({summary: 'Find employee' }) -// // @ApiResponse({ status: 200, description: 'Employee found', type: CreateEmployeeDto }) -// // @ApiResponse({ status: 400, description: 'Employee not found' }) -// // findOne(@Param('email', ParseIntPipe) email: string): Promise { -// // return this.employeesService.findOne(email); -// // } + // @Get(':email') + // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR,RoleEnum.ACCOUNTING ) + // @ApiOperation({summary: 'Find employee' }) + // @ApiResponse({ status: 200, description: 'Employee found', type: CreateEmployeeDto }) + // @ApiResponse({ status: 400, description: 'Employee not found' }) + // findOne(@Param('email', ParseIntPipe) email: string): Promise { + // return this.employeesService.findOne(email); + // } -// @Get('profile/:email') -// @ApiOperation({summary: 'Find employee profile' }) -// @ApiParam({ name: 'email', type: String, description: 'Identifier of the employee' }) -// @ApiResponse({ status: 200, description: 'Employee profile found', type: EmployeeProfileItemDto }) -// @ApiResponse({ status: 400, description: 'Employee profile not found' }) -// findOneProfile(@Param('email') email: string): Promise { -// return this.employeesService.findOneProfile(email); -// } + @Get('profile/:email') + @ApiOperation({summary: 'Find employee profile' }) + @ApiParam({ name: 'email', type: String, description: 'Identifier of the employee' }) + @ApiResponse({ status: 200, description: 'Employee profile found', type: EmployeeProfileItemDto }) + @ApiResponse({ status: 400, description: 'Employee profile not found' }) + findOneProfile(@Param('email') email: string): Promise { + return this.employeesService.findOneProfile(email); + } -// // @Delete(':email') -// // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR ) -// // @ApiOperation({summary: 'Delete employee' }) -// // @ApiParam({ name: 'email', type: Number, description: 'Email of the employee to delete' }) -// // @ApiResponse({ status: 204, description: 'Employee deleted' }) -// // @ApiResponse({ status: 404, description: 'Employee not found' }) -// // remove(@Param('email', ParseIntPipe) email: string): Promise { -// // return this.employeesService.remove(email); -// // } + // @Delete(':email') + // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR ) + // @ApiOperation({summary: 'Delete employee' }) + // @ApiParam({ name: 'email', type: Number, description: 'Email of the employee to delete' }) + // @ApiResponse({ status: 204, description: 'Employee deleted' }) + // @ApiResponse({ status: 404, description: 'Employee not found' }) + // remove(@Param('email', ParseIntPipe) email: string): Promise { + // return this.employeesService.remove(email); + // } -// } +} diff --git a/src/identity-and-account/employees/dtos/create-employee.dto.ts b/src/identity-and-account/employees/dtos/create-employee.dto.ts index 4fbbaaa..89279ef 100644 --- a/src/identity-and-account/employees/dtos/create-employee.dto.ts +++ b/src/identity-and-account/employees/dtos/create-employee.dto.ts @@ -1,118 +1,118 @@ -// import { -// Allow, -// IsBoolean, -// IsDateString, -// IsEmail, -// IsInt, -// IsNotEmpty, -// IsOptional, -// IsPositive, -// IsString, -// IsUUID, -// } from 'class-validator'; -// import { Type } from 'class-transformer'; -// import { ApiProperty } from '@nestjs/swagger'; +import { + Allow, + IsBoolean, + IsDateString, + IsEmail, + IsInt, + IsNotEmpty, + IsOptional, + IsPositive, + IsString, + IsUUID, +} from 'class-validator'; +import { Type } from 'class-transformer'; +import { ApiProperty } from '@nestjs/swagger'; -// export class CreateEmployeeDto { -// @ApiProperty({ -// example: 1, -// description: 'Unique ID of an employee(primary-key, auto-incremented)', -// }) -// @Allow() -// id: number; +export class CreateEmployeeDto { + @ApiProperty({ + example: 1, + description: 'Unique ID of an employee(primary-key, auto-incremented)', + }) + @Allow() + id: number; -// @ApiProperty({ -// example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d', -// description: 'UUID of the user linked to that employee', -// }) -// @IsUUID() -// @IsOptional() -// user_id?: string; + @ApiProperty({ + example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d', + description: 'UUID of the user linked to that employee', + }) + @IsUUID() + @IsOptional() + user_id?: string; -// @ApiProperty({ -// example: 'Frodo', -// description: 'Employee`s first name', -// }) -// @IsString() -// @IsNotEmpty() -// first_name: string; + @ApiProperty({ + example: 'Frodo', + description: 'Employee`s first name', + }) + @IsString() + @IsNotEmpty() + first_name: string; -// @ApiProperty({ -// example: 'Baggins', -// description: 'Employee`s last name', -// }) -// @IsString() -// @IsNotEmpty() -// last_name: string; + @ApiProperty({ + example: 'Baggins', + description: 'Employee`s last name', + }) + @IsString() + @IsNotEmpty() + last_name: string; -// @ApiProperty({ -// example: 'i_cant_do_this_sam@targointernet.com', -// description: 'Employee`s email', -// }) -// @IsEmail() -// @IsOptional() -// email: string; + @ApiProperty({ + example: 'i_cant_do_this_sam@targointernet.com', + description: 'Employee`s email', + }) + @IsEmail() + @IsOptional() + email: string; -// @IsOptional() -// @IsBoolean() -// is_supervisor: boolean; + @IsOptional() + @IsBoolean() + is_supervisor: boolean; -// @ApiProperty({ -// example: '82538437464', -// description: 'Employee`s phone number', -// }) -// @IsString() -// phone_number: string; + @ApiProperty({ + example: '82538437464', + description: 'Employee`s phone number', + }) + @IsString() + phone_number: string; -// @ApiProperty({ -// example: '1 Bagshot Row, Hobbiton, The Shire, Middle-earth', -// description: 'Employee`s residence', -// required: false, -// }) -// @IsString() -// @IsOptional() -// residence?: string; + @ApiProperty({ + example: '1 Bagshot Row, Hobbiton, The Shire, Middle-earth', + description: 'Employee`s residence', + required: false, + }) + @IsString() + @IsOptional() + residence?: string; -// @ApiProperty({ -// example: 7464, -// description: 'external ID for the pay system', -// }) -// @IsInt() -// @IsPositive() -// @Type(() => Number) -// external_payroll_id: number; + @ApiProperty({ + example: 7464, + description: 'external ID for the pay system', + }) + @IsInt() + @IsPositive() + @Type(() => Number) + external_payroll_id: number; -// @ApiProperty({ -// example: 335567447, -// description: 'Employee`s company code', -// }) -// @IsInt() -// @IsPositive() -// @Type(() => Number) -// company_code: number; + @ApiProperty({ + example: 335567447, + description: 'Employee`s company code', + }) + @IsInt() + @IsPositive() + @Type(() => Number) + company_code: number; -// @ApiProperty({ -// example:'technicient', -// description: 'employee`s job title', -// }) -// @IsString() -// @IsOptional() -// job_title: string; + @ApiProperty({ + example:'technicient', + description: 'employee`s job title', + }) + @IsString() + @IsOptional() + job_title: string; -// @ApiProperty({ -// example: '23/09/3018', -// description: 'Employee`s first working day', -// }) -// @IsDateString() -// first_work_day: string; + @ApiProperty({ + example: '23/09/3018', + description: 'Employee`s first working day', + }) + @IsDateString() + first_work_day: string; -// @ApiProperty({ -// example: '25/03/3019', -// description: 'Employee`s last working day', -// required: false, -// }) -// @IsDateString() -// @IsOptional() -// last_work_day?: string; -// } + @ApiProperty({ + example: '25/03/3019', + description: 'Employee`s last working day', + required: false, + }) + @IsDateString() + @IsOptional() + last_work_day?: string; +} diff --git a/src/identity-and-account/employees/dtos/list-employee.dto.ts b/src/identity-and-account/employees/dtos/list-employee.dto.ts index 6adbe3f..39abf03 100644 --- a/src/identity-and-account/employees/dtos/list-employee.dto.ts +++ b/src/identity-and-account/employees/dtos/list-employee.dto.ts @@ -1,8 +1,8 @@ -// export class EmployeeListItemDto { -// first_name: string; -// last_name: string; -// email: string; -// supervisor_full_name: string | null; -// company_name: number | null; -// job_title: string | null; -// } \ No newline at end of file +export class EmployeeListItemDto { + first_name: string; + last_name: string; + email: string; + supervisor_full_name: string | null; + company_name: number | null; + job_title: string | null; +} \ No newline at end of file diff --git a/src/identity-and-account/employees/dtos/profil-employee.dto.ts b/src/identity-and-account/employees/dtos/profil-employee.dto.ts index adbf38e..c6836cf 100644 --- a/src/identity-and-account/employees/dtos/profil-employee.dto.ts +++ b/src/identity-and-account/employees/dtos/profil-employee.dto.ts @@ -1,13 +1,13 @@ -// export class EmployeeProfileItemDto { -// first_name: string; -// last_name: string; -// employee_full_name: string; -// supervisor_full_name: string | null; -// company_name: number | null; -// job_title: string | null; -// email: string | null; -// phone_number: string; -// first_work_day: string; -// last_work_day?: string | null; -// residence: string | null; -// } \ No newline at end of file +export class EmployeeProfileItemDto { + first_name: string; + last_name: string; + employee_full_name: string; + supervisor_full_name: string | null; + company_name: number | null; + job_title: string | null; + email: string | null; + phone_number: string; + first_work_day: string; + last_work_day?: string | null; + residence: string | null; +} \ No newline at end of file diff --git a/src/identity-and-account/employees/dtos/update-employee.dto.ts b/src/identity-and-account/employees/dtos/update-employee.dto.ts index 1efbbfd..334a01a 100644 --- a/src/identity-and-account/employees/dtos/update-employee.dto.ts +++ b/src/identity-and-account/employees/dtos/update-employee.dto.ts @@ -1,22 +1,22 @@ -// import { ApiProperty, PartialType } from '@nestjs/swagger'; -// import { CreateEmployeeDto } from './create-employee.dto'; -// import { IsDateString, IsOptional, Max } from 'class-validator'; +import { ApiProperty, PartialType } from '@nestjs/swagger'; +import { CreateEmployeeDto } from './create-employee.dto'; +import { IsDateString, IsOptional, Max } from 'class-validator'; -// export class UpdateEmployeeDto extends PartialType(CreateEmployeeDto) { -// @ApiProperty({ required: false, type: Date, description: 'New hire date or undefined' }) -// @IsDateString() -// @IsOptional() -// first_work_day?: string; +export class UpdateEmployeeDto extends PartialType(CreateEmployeeDto) { + @ApiProperty({ required: false, type: Date, description: 'New hire date or undefined' }) + @IsDateString() + @IsOptional() + first_work_day?: string; -// @ApiProperty({ required: false, type: Date, description: 'Termination date (null to restore)' }) -// @IsDateString() -// @IsOptional() -// last_work_day?: string; + @ApiProperty({ required: false, type: Date, description: 'Termination date (null to restore)' }) + @IsDateString() + @IsOptional() + last_work_day?: string; -// @ApiProperty({ required: false, type: Number, description: 'Supervisor ID' }) -// @IsOptional() -// supervisor_id?: number; + @ApiProperty({ required: false, type: Number, description: 'Supervisor ID' }) + @IsOptional() + supervisor_id?: number; -// @IsOptional() -// phone_number: string; -// } + @IsOptional() + phone_number: string; +} diff --git a/src/identity-and-account/employees/services/employees-archival.service.ts b/src/identity-and-account/employees/services/employees-archival.service.ts index 6046e94..2aa184a 100644 --- a/src/identity-and-account/employees/services/employees-archival.service.ts +++ b/src/identity-and-account/employees/services/employees-archival.service.ts @@ -1,173 +1,173 @@ -// import { Injectable } from "@nestjs/common"; -// import { Employees, EmployeesArchive, Users } from "@prisma/client"; -// import { PrismaService } from "src/prisma/prisma.service"; -// import { UpdateEmployeeDto } from "../dtos/update-employee.dto"; -// import { toDateOrUndefined, toDateOrNull } from "../utils/employee.utils"; +import { Injectable } from "@nestjs/common"; +import { Employees, Users } from "@prisma/client"; +import { UpdateEmployeeDto } from "src/identity-and-account/employees/dtos/update-employee.dto"; +import { toDateOrUndefined, toDateOrNull } from "src/identity-and-account/employees/utils/employee.utils"; +import { PrismaService } from "src/prisma/prisma.service"; -// @Injectable() -// export class EmployeesArchivalService { -// constructor(private readonly prisma: PrismaService) { } +@Injectable() +export class EmployeesArchivalService { + constructor(private readonly prisma: PrismaService) { } -// async patchEmployee(email: string, dto: UpdateEmployeeDto): Promise { -// // 1) Tenter sur employés actifs -// const active = await this.prisma.employees.findFirst({ -// where: { user: { email } }, -// include: { user: true }, -// }); + async patchEmployee(email: string, dto: UpdateEmployeeDto): Promise { + // 1) Tenter sur employés actifs + const active = await this.prisma.employees.findFirst({ + where: { user: { email } }, + include: { user: true }, + }); -// if (active) { -// // Archivage : si on reçoit un last_work_day défini et que l'employé n’est pas déjà terminé -// if (dto.last_work_day !== undefined && active.last_work_day == null && dto.last_work_day !== null) { -// return this.archiveOnTermination(active, dto); -// } + if (active) { + // Archivage : si on reçoit un last_work_day défini et que l'employé n’est pas déjà terminé + // if (dto.last_work_day !== undefined && active.last_work_day == null && dto.last_work_day !== null) { + // return this.archiveOnTermination(active, dto); + // } -// // Sinon, update standard (split Users/Employees) -// const { -// first_name, -// last_name, -// email: new_email, -// phone_number, -// residence, -// external_payroll_id, -// company_code, -// job_title, -// first_work_day, -// last_work_day, -// supervisor_id, -// is_supervisor, -// } = dto as any; + // Sinon, update standard (split Users/Employees) + const { + first_name, + last_name, + email: new_email, + phone_number, + residence, + external_payroll_id, + company_code, + job_title, + first_work_day, + last_work_day, + supervisor_id, + is_supervisor, + } = dto as any; -// const first_work_d = toDateOrUndefined(first_work_day); -// const last_work_d = Object.prototype.hasOwnProperty('last_work_day') -// ? toDateOrNull(last_work_day ?? null) -// : undefined; + const first_work_d = toDateOrUndefined(first_work_day); + const last_work_d = Object.prototype.hasOwnProperty('last_work_day') + ? toDateOrNull(last_work_day ?? null) + : undefined; -// await this.prisma.$transaction(async (transaction) => { -// if ( -// first_name !== undefined || -// last_name !== undefined || -// new_email !== undefined || -// phone_number !== undefined || -// residence !== undefined -// ) { -// await transaction.users.update({ -// where: { id: active.user_id }, -// data: { -// ...(first_name !== undefined ? { first_name } : {}), -// ...(last_name !== undefined ? { last_name } : {}), -// ...(email !== undefined ? { email: new_email } : {}), -// ...(phone_number !== undefined ? { phone_number } : {}), -// ...(residence !== undefined ? { residence } : {}), -// }, -// }); + await this.prisma.$transaction(async (transaction) => { + if ( + first_name !== undefined || + last_name !== undefined || + new_email !== undefined || + phone_number !== undefined || + residence !== undefined + ) { + await transaction.users.update({ + where: { id: active.user_id }, + data: { + ...(first_name !== undefined ? { first_name } : {}), + ...(last_name !== undefined ? { last_name } : {}), + ...(email !== undefined ? { email: new_email } : {}), + ...(phone_number !== undefined ? { phone_number } : {}), + ...(residence !== undefined ? { residence } : {}), + }, + }); -// } + } -// const updated = await transaction.employees.update({ -// where: { id: active.id }, -// data: { -// ...(external_payroll_id !== undefined ? { external_payroll_id } : {}), -// ...(company_code !== undefined ? { company_code } : {}), -// ...(job_title !== undefined ? { job_title } : {}), -// ...(first_work_d !== undefined ? { first_work_day: first_work_d } : {}), -// ...(last_work_d !== undefined ? { last_work_day: last_work_d } : {}), -// ...(is_supervisor !== undefined ? { is_supervisor } : {}), -// ...(supervisor_id !== undefined ? { supervisor_id } : {}), -// }, -// include: { user: true }, -// }); + const updated = await transaction.employees.update({ + where: { id: active.id }, + data: { + ...(external_payroll_id !== undefined ? { external_payroll_id } : {}), + ...(company_code !== undefined ? { company_code } : {}), + ...(job_title !== undefined ? { job_title } : {}), + ...(first_work_d !== undefined ? { first_work_day: first_work_d } : {}), + ...(last_work_d !== undefined ? { last_work_day: last_work_d } : {}), + ...(is_supervisor !== undefined ? { is_supervisor } : {}), + ...(supervisor_id !== undefined ? { supervisor_id } : {}), + }, + include: { user: true }, + }); -// return updated; -// }); + return updated; + }); -// return this.prisma.employees.findFirst({ where: { user: { email } } }); -// } + return this.prisma.employees.findFirst({ where: { user: { email } } }); + } -// const user = await this.prisma.users.findUnique({ where: { email } }); -// if (!user) return null; -// // 2) Pas trouvé en actifs → regarder en archive (pour restauration) -// const archived = await this.prisma.employeesArchive.findFirst({ -// where: { user_id: user.id }, -// include: { user: true }, -// }); + const user = await this.prisma.users.findUnique({ where: { email } }); + if (!user) return null; + // 2) Pas trouvé en actifs → regarder en archive (pour restauration) + // const archived = await this.prisma.employeesArchive.findFirst({ + // where: { user_id: user.id }, + // include: { user: true }, + // }); -// if (archived) { -// // Condition de restauration : last_work_day === null ou first_work_day fourni -// const restore = dto.last_work_day === null || dto.first_work_day != null; -// if (restore) { -// return this.restoreEmployee(archived, dto); -// } -// } -// // 3) Ni actif, ni archivé → 404 dans le controller -// return null; -// } + // if (archived) { + // // Condition de restauration : last_work_day === null ou first_work_day fourni + // const restore = dto.last_work_day === null || dto.first_work_day != null; + // if (restore) { + // return this.restoreEmployee(archived, dto); + // } + // } + // 3) Ni actif, ni archivé → 404 dans le controller + return null; + } -// //transfers the employee to archive and then delete from employees table -// private async archiveOnTermination(active: Employees & { user: Users }, dto: UpdateEmployeeDto): Promise { -// const last_work_d = toDateOrNull(dto.last_work_day!); -// if (!last_work_d) throw new Error('invalide last_work_day for archive'); -// return this.prisma.$transaction(async transaction => { -// //detach crew from supervisor if employee is a supervisor -// await transaction.employees.updateMany({ -// where: { supervisor_id: active.id }, -// data: { supervisor_id: null }, -// }) -// const archived = await transaction.employeesArchive.create({ -// data: { -// employee_id: active.id, -// user_id: active.user_id, -// first_name: active.user.first_name, -// last_name: active.user.last_name, -// company_code: active.company_code, -// job_title: active.job_title, -// first_work_day: active.first_work_day, -// last_work_day: last_work_d, -// supervisor_id: active.supervisor_id ?? null, -// is_supervisor: active.is_supervisor, -// external_payroll_id: active.external_payroll_id, -// }, -// include: { user: true } -// }); -// //delete from employees table -// await transaction.employees.delete({ where: { id: active.id } }); -// //return archived employee -// return archived -// }); -// } + //transfers the employee to archive and then delete from employees table + // private async archiveOnTermination(active: Employees & { user: Users }, dto: UpdateEmployeeDto): Promise { + // const last_work_d = toDateOrNull(dto.last_work_day!); + // if (!last_work_d) throw new Error('invalide last_work_day for archive'); + // return this.prisma.$transaction(async transaction => { + // //detach crew from supervisor if employee is a supervisor + // await transaction.employees.updateMany({ + // where: { supervisor_id: active.id }, + // data: { supervisor_id: null }, + // }) + // const archived = await transaction.employeesArchive.create({ + // data: { + // employee_id: active.id, + // user_id: active.user_id, + // first_name: active.user.first_name, + // last_name: active.user.last_name, + // company_code: active.company_code, + // job_title: active.job_title, + // first_work_day: active.first_work_day, + // last_work_day: last_work_d, + // supervisor_id: active.supervisor_id ?? null, + // is_supervisor: active.is_supervisor, + // external_payroll_id: active.external_payroll_id, + // }, + // include: { user: true } + // }); + // //delete from employees table + // await transaction.employees.delete({ where: { id: active.id } }); + // //return archived employee + // return archived + // }); + // } -// //transfers the employee from archive to the employees table -// private async restoreEmployee(archived: EmployeesArchive & { user: Users }, dto: UpdateEmployeeDto): Promise { -// // const first_work_d = toDateOrUndefined(dto.first_work_day); -// return this.prisma.$transaction(async transaction => { -// //restores the archived employee into the employees table -// const restored = await transaction.employees.create({ -// data: { -// user_id: archived.user_id, -// company_code: archived.company_code, -// job_title: archived.job_title, -// first_work_day: archived.first_work_day, -// last_work_day: null, -// is_supervisor: archived.is_supervisor ?? false, -// external_payroll_id: archived.external_payroll_id, -// }, -// }); -// //deleting archived entry by id -// await transaction.employeesArchive.delete({ where: { id: archived.id } }); + // //transfers the employee from archive to the employees table + // private async restoreEmployee(archived: EmployeesArchive & { user: Users }, dto: UpdateEmployeeDto): Promise { + // // const first_work_d = toDateOrUndefined(dto.first_work_day); + // return this.prisma.$transaction(async transaction => { + // //restores the archived employee into the employees table + // const restored = await transaction.employees.create({ + // data: { + // user_id: archived.user_id, + // company_code: archived.company_code, + // job_title: archived.job_title, + // first_work_day: archived.first_work_day, + // last_work_day: null, + // is_supervisor: archived.is_supervisor ?? false, + // external_payroll_id: archived.external_payroll_id, + // }, + // }); + // //deleting archived entry by id + // await transaction.employeesArchive.delete({ where: { id: archived.id } }); -// //return restored employee -// return restored; -// }); -// } + // //return restored employee + // return restored; + // }); + // } -// //fetches all archived employees -// async findAllArchived(): Promise { -// return this.prisma.employeesArchive.findMany(); -// } + // //fetches all archived employees + // async findAllArchived(): Promise { + // return this.prisma.employeesArchive.findMany(); + // } -// //fetches an archived employee -// async findOneArchived(id: number): Promise { -// return this.prisma.employeesArchive.findUniqueOrThrow({ where: { id } }); -// } + // //fetches an archived employee + // async findOneArchived(id: number): Promise { + // return this.prisma.employeesArchive.findUniqueOrThrow({ where: { id } }); + // } -// } +} diff --git a/src/identity-and-account/employees/services/employees.service.ts b/src/identity-and-account/employees/services/employees.service.ts index b44bf7d..50873b6 100644 --- a/src/identity-and-account/employees/services/employees.service.ts +++ b/src/identity-and-account/employees/services/employees.service.ts @@ -1,230 +1,230 @@ -// import { Injectable, NotFoundException } from '@nestjs/common'; -// import { PrismaService } from 'src/prisma/prisma.service'; -// import { EmployeeListItemDto } from '../dtos/list-employee.dto'; -// import { EmployeeProfileItemDto } from '../dtos/profil-employee.dto'; +import { Injectable, NotFoundException } from "@nestjs/common"; +import { EmployeeListItemDto } from "src/identity-and-account/employees/dtos/list-employee.dto"; +import { EmployeeProfileItemDto } from "src/identity-and-account/employees/dtos/profil-employee.dto"; +import { PrismaService } from "src/prisma/prisma.service"; -// @Injectable() -// export class EmployeesService { -// constructor(private readonly prisma: PrismaService) { } +@Injectable() +export class EmployeesService { + constructor(private readonly prisma: PrismaService) { } -// findListEmployees(): Promise { -// return this.prisma.employees.findMany({ -// select: { -// user: { -// select: { -// first_name: true, -// last_name: true, -// email: true, -// }, -// }, -// supervisor: { -// select: { -// user: { -// select: { -// first_name: true, -// last_name: true, -// }, -// }, -// }, -// }, -// job_title: true, -// company_code: true, -// } -// }).then(rows => rows.map(r => ({ -// first_name: r.user.first_name, -// last_name: r.user.last_name, -// email: r.user.email, -// company_name: r.company_code, -// job_title: r.job_title, -// employee_full_name: `${r.user.first_name} ${r.user.last_name}`, -// supervisor_full_name: r.supervisor ? `${r.supervisor.user.first_name} ${r.supervisor.user.last_name}` : null, -// })), -// ); -// } + findListEmployees(): Promise { + return this.prisma.employees.findMany({ + select: { + user: { + select: { + first_name: true, + last_name: true, + email: true, + }, + }, + supervisor: { + select: { + user: { + select: { + first_name: true, + last_name: true, + }, + }, + }, + }, + job_title: true, + company_code: true, + } + }).then(rows => rows.map(r => ({ + first_name: r.user.first_name, + last_name: r.user.last_name, + email: r.user.email, + company_name: r.company_code, + job_title: r.job_title, + employee_full_name: `${r.user.first_name} ${r.user.last_name}`, + supervisor_full_name: r.supervisor ? `${r.supervisor.user.first_name} ${r.supervisor.user.last_name}` : null, + })), + ); + } -// async findOneProfile(email: string): Promise { -// const emp = await this.prisma.employees.findFirst({ -// where: { user: { email } }, -// select: { -// user: { -// select: { -// first_name: true, -// last_name: true, -// email: true, -// phone_number: true, -// residence: true, -// }, -// }, -// supervisor: { -// select: { -// user: { -// select: { -// first_name: true, -// last_name: true, -// }, -// }, -// }, -// }, -// job_title: true, -// company_code: true, -// first_work_day: true, -// last_work_day: true, -// } -// }); -// if (!emp) throw new NotFoundException(`Employee with email ${email} not found`); + async findOneProfile(email: string): Promise { + const emp = await this.prisma.employees.findFirst({ + where: { user: { email } }, + select: { + user: { + select: { + first_name: true, + last_name: true, + email: true, + phone_number: true, + residence: true, + }, + }, + supervisor: { + select: { + user: { + select: { + first_name: true, + last_name: true, + }, + }, + }, + }, + job_title: true, + company_code: true, + first_work_day: true, + last_work_day: true, + } + }); + if (!emp) throw new NotFoundException(`Employee with email ${email} not found`); -// return { -// first_name: emp.user.first_name, -// last_name: emp.user.last_name, -// email: emp.user.email, -// residence: emp.user.residence, -// phone_number: emp.user.phone_number, -// company_name: emp.company_code, -// job_title: emp.job_title, -// employee_full_name: `${emp.user.first_name} ${emp.user.last_name}`, -// first_work_day: emp.first_work_day.toISOString().slice(0, 10), -// last_work_day: emp.last_work_day ? emp.last_work_day.toISOString().slice(0, 10) : null, -// supervisor_full_name: emp.supervisor ? `${emp.supervisor.user.first_name}, ${emp.supervisor.user.last_name}` : null, -// }; -// } + return { + first_name: emp.user.first_name, + last_name: emp.user.last_name, + email: emp.user.email, + residence: emp.user.residence, + phone_number: emp.user.phone_number, + company_name: emp.company_code, + job_title: emp.job_title, + employee_full_name: `${emp.user.first_name} ${emp.user.last_name}`, + first_work_day: emp.first_work_day.toISOString().slice(0, 10), + last_work_day: emp.last_work_day ? emp.last_work_day.toISOString().slice(0, 10) : null, + supervisor_full_name: emp.supervisor ? `${emp.supervisor.user.first_name}, ${emp.supervisor.user.last_name}` : null, + }; + } -// //_____________________________________________________________________________________________ -// // Deprecated or unused methods -// //_____________________________________________________________________________________________ + //_____________________________________________________________________________________________ + // Deprecated or unused methods + //_____________________________________________________________________________________________ -// // async create(dto: CreateEmployeeDto): Promise { -// // const { -// // first_name, -// // last_name, -// // email, -// // phone_number, -// // residence, -// // external_payroll_id, -// // company_code, -// // job_title, -// // first_work_day, -// // last_work_day, -// // is_supervisor, -// // } = dto; + // async create(dto: CreateEmployeeDto): Promise { + // const { + // first_name, + // last_name, + // email, + // phone_number, + // residence, + // external_payroll_id, + // company_code, + // job_title, + // first_work_day, + // last_work_day, + // is_supervisor, + // } = dto; -// // return this.prisma.$transaction(async (transaction) => { -// // const user: Users = await transaction.users.create({ -// // data: { -// // first_name, -// // last_name, -// // email, -// // phone_number, -// // residence, -// // }, -// // }); -// // return transaction.employees.create({ -// // data: { -// // user_id: user.id, -// // external_payroll_id, -// // company_code, -// // job_title, -// // first_work_day, -// // last_work_day, -// // is_supervisor, -// // }, -// // }); -// // }); -// // } + // return this.prisma.$transaction(async (transaction) => { + // const user: Users = await transaction.users.create({ + // data: { + // first_name, + // last_name, + // email, + // phone_number, + // residence, + // }, + // }); + // return transaction.employees.create({ + // data: { + // user_id: user.id, + // external_payroll_id, + // company_code, + // job_title, + // first_work_day, + // last_work_day, + // is_supervisor, + // }, + // }); + // }); + // } -// // findAll(): Promise { -// // return this.prisma.employees.findMany({ -// // include: { user: true }, -// // }); -// // } + // findAll(): Promise { + // return this.prisma.employees.findMany({ + // include: { user: true }, + // }); + // } -// // async findOne(email: string): Promise { -// // const emp = await this.prisma.employees.findFirst({ -// // where: { user: { email } }, -// // include: { user: true }, -// // }); + // async findOne(email: string): Promise { + // const emp = await this.prisma.employees.findFirst({ + // where: { user: { email } }, + // include: { user: true }, + // }); -// // //add search for archived employees -// // if (!emp) { -// // throw new NotFoundException(`Employee with email: ${email} not found`); -// // } -// // return emp; -// // } + // //add search for archived employees + // if (!emp) { + // throw new NotFoundException(`Employee with email: ${email} not found`); + // } + // return emp; + // } -// // async update( -// // email: string, -// // dto: UpdateEmployeeDto, -// // ): Promise { -// // const emp = await this.findOne(email); + // async update( + // email: string, + // dto: UpdateEmployeeDto, + // ): Promise { + // const emp = await this.findOne(email); -// // const { -// // first_name, -// // last_name, -// // phone_number, -// // residence, -// // external_payroll_id, -// // company_code, -// // job_title, -// // first_work_day, -// // last_work_day, -// // is_supervisor, -// // email: new_email, -// // } = dto; + // const { + // first_name, + // last_name, + // phone_number, + // residence, + // external_payroll_id, + // company_code, + // job_title, + // first_work_day, + // last_work_day, + // is_supervisor, + // email: new_email, + // } = dto; -// // return this.prisma.$transaction(async (transaction) => { -// // if( -// // first_name !== undefined || -// // last_name !== undefined || -// // new_email !== undefined || -// // phone_number !== undefined || -// // residence !== undefined -// // ){ -// // await transaction.users.update({ -// // where: { id: emp.user_id }, -// // data: { -// // ...(first_name !== undefined && { first_name }), -// // ...(last_name !== undefined && { last_name }), -// // ...(email !== undefined && { email }), -// // ...(phone_number !== undefined && { phone_number }), -// // ...(residence !== undefined && { residence }), -// // }, -// // }); -// // } + // return this.prisma.$transaction(async (transaction) => { + // if( + // first_name !== undefined || + // last_name !== undefined || + // new_email !== undefined || + // phone_number !== undefined || + // residence !== undefined + // ){ + // await transaction.users.update({ + // where: { id: emp.user_id }, + // data: { + // ...(first_name !== undefined && { first_name }), + // ...(last_name !== undefined && { last_name }), + // ...(email !== undefined && { email }), + // ...(phone_number !== undefined && { phone_number }), + // ...(residence !== undefined && { residence }), + // }, + // }); + // } -// // const updated = await transaction.employees.update({ -// // where: { id: emp.id }, -// // data: { -// // ...(external_payroll_id !== undefined && { external_payroll_id }), -// // ...(company_code !== undefined && { company_code }), -// // ...(first_work_day !== undefined && { first_work_day }), -// // ...(last_work_day !== undefined && { last_work_day }), -// // ...(job_title !== undefined && { job_title }), -// // ...(is_supervisor !== undefined && { is_supervisor }), -// // }, -// // }); -// // return updated; -// // }); -// // } + // const updated = await transaction.employees.update({ + // where: { id: emp.id }, + // data: { + // ...(external_payroll_id !== undefined && { external_payroll_id }), + // ...(company_code !== undefined && { company_code }), + // ...(first_work_day !== undefined && { first_work_day }), + // ...(last_work_day !== undefined && { last_work_day }), + // ...(job_title !== undefined && { job_title }), + // ...(is_supervisor !== undefined && { is_supervisor }), + // }, + // }); + // return updated; + // }); + // } -// // async remove(email: string): Promise { + // async remove(email: string): Promise { -// // const emp = await this.findOne(email); + // const emp = await this.findOne(email); -// // return this.prisma.$transaction(async (transaction) => { -// // await transaction.employees.updateMany({ -// // where: { supervisor_id: emp.id }, -// // data: { supervisor_id: null }, -// // }); -// // const deleted_employee = await transaction.employees.delete({ -// // where: {id: emp.id }, -// // }); -// // await transaction.users.delete({ -// // where: { id: emp.user_id }, -// // }); -// // return deleted_employee; -// // }); -// // } + // return this.prisma.$transaction(async (transaction) => { + // await transaction.employees.updateMany({ + // where: { supervisor_id: emp.id }, + // data: { supervisor_id: null }, + // }); + // const deleted_employee = await transaction.employees.delete({ + // where: {id: emp.id }, + // }); + // await transaction.users.delete({ + // where: { id: emp.user_id }, + // }); + // return deleted_employee; + // }); + // } -// } \ No newline at end of file +} \ No newline at end of file diff --git a/src/identity-and-account/employees/utils/employee.utils.ts b/src/identity-and-account/employees/utils/employee.utils.ts index 04f2540..3534f3d 100644 --- a/src/identity-and-account/employees/utils/employee.utils.ts +++ b/src/identity-and-account/employees/utils/employee.utils.ts @@ -1,9 +1,9 @@ -// export function toDateOrNull(v?: string | null): Date | null { -// if (!v) return null; -// const day = new Date(v); -// return isNaN(day.getTime()) ? null : day; -// } -// export function toDateOrUndefined(v?: string | null): Date | undefined { -// const day = toDateOrNull(v ?? undefined); -// return day === null ? undefined : day; -// } \ No newline at end of file +export function toDateOrNull(v?: string | null): Date | null { + if (!v) return null; + const day = new Date(v); + return isNaN(day.getTime()) ? null : day; +} +export function toDateOrUndefined(v?: string | null): Date | undefined { + const day = toDateOrNull(v ?? undefined); + return day === null ? undefined : day; +} \ No newline at end of file From 1a0532846f9d9ad50a90747a711b284b171d815d Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Wed, 5 Nov 2025 14:27:54 -0500 Subject: [PATCH 46/57] feat(role-guards): added role-guards group and added role check to controllers --- src/common/guards/ownership.guard.ts | 10 +- src/common/guards/roles.guard.ts | 6 +- src/common/shared/role-groupes.ts | 15 +++ .../controllers/employees.controller.ts | 24 ++--- .../services/abstract-user.service.ts | 58 +++++----- .../controller/schedule-presets.controller.ts | 29 ++--- .../shifts/controllers/shift.controller.ts | 11 +- .../shifts/services/shifts-upsert.service.ts | 100 +++++++++--------- .../controllers/timesheet.controller.ts | 25 +++-- 9 files changed, 146 insertions(+), 132 deletions(-) create mode 100644 src/common/shared/role-groupes.ts diff --git a/src/common/guards/ownership.guard.ts b/src/common/guards/ownership.guard.ts index 9fcba18..27928bc 100644 --- a/src/common/guards/ownership.guard.ts +++ b/src/common/guards/ownership.guard.ts @@ -1,4 +1,4 @@ -import { +import { CanActivate, Injectable, ExecutionContext, @@ -17,15 +17,15 @@ export class OwnershipGuard implements CanActivate { constructor( private reflector: Reflector, private moduleRef: ModuleRef, - ) {} + ) { } - async canActivate(context: ExecutionContext): Promise{ + async canActivate(context: ExecutionContext): Promise { const meta = this.reflector.get( OWNER_KEY, context.getHandler(), ); - if (!meta) + if (!meta) return true; - + const request = context.switchToHttp().getRequest(); const user = request.user; const resourceId = request.params[meta.idParam || 'id']; diff --git a/src/common/guards/roles.guard.ts b/src/common/guards/roles.guard.ts index 61889f7..9a9244d 100644 --- a/src/common/guards/roles.guard.ts +++ b/src/common/guards/roles.guard.ts @@ -17,7 +17,7 @@ interface RequestWithUser extends Request { @Injectable() export class RolesGuard implements CanActivate { - constructor(private reflector: Reflector) {} + constructor(private reflector: Reflector) { } /** * @swagger @@ -37,9 +37,9 @@ export class RolesGuard implements CanActivate { * or returns `false` if the user is not authenticated. */ canActivate(ctx: ExecutionContext): boolean { - const requiredRoles = this.reflector.get( + const requiredRoles = this.reflector.getAllAndOverride( ROLES_KEY, - ctx.getHandler(), + [ctx.getHandler(), ctx.getClass()], ); //for "deny-by-default" when role is wrong or unavailable if (!requiredRoles || requiredRoles.length === 0) { diff --git a/src/common/shared/role-groupes.ts b/src/common/shared/role-groupes.ts new file mode 100644 index 0000000..b67c432 --- /dev/null +++ b/src/common/shared/role-groupes.ts @@ -0,0 +1,15 @@ +import { Roles as RoleEnum } from ".prisma/client"; + +export const GLOBAL_CONTROLLER_ROLES: readonly RoleEnum[] = [ + RoleEnum.EMPLOYEE, + RoleEnum.ACCOUNTING, + RoleEnum.HR, + RoleEnum.SUPERVISOR, + RoleEnum.ADMIN, +]; + +export const MANAGER_ROLES: readonly RoleEnum[] = [ + RoleEnum.HR, + RoleEnum.SUPERVISOR, + RoleEnum.ADMIN, +] \ No newline at end of file diff --git a/src/identity-and-account/employees/controllers/employees.controller.ts b/src/identity-and-account/employees/controllers/employees.controller.ts index dabca96..fa45089 100644 --- a/src/identity-and-account/employees/controllers/employees.controller.ts +++ b/src/identity-and-account/employees/controllers/employees.controller.ts @@ -1,37 +1,35 @@ import { Controller, Get, Patch, Param, Body, NotFoundException } from "@nestjs/common"; -import { ApiBearerAuth, ApiOperation, ApiResponse, ApiParam } from "@nestjs/swagger"; +import { RolesAllowed } from "src/common/decorators/roles.decorators"; +import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes"; import { EmployeeListItemDto } from "src/identity-and-account/employees/dtos/list-employee.dto"; import { EmployeeProfileItemDto } from "src/identity-and-account/employees/dtos/profil-employee.dto"; import { UpdateEmployeeDto } from "src/identity-and-account/employees/dtos/update-employee.dto"; import { EmployeesArchivalService } from "src/identity-and-account/employees/services/employees-archival.service"; import { EmployeesService } from "src/identity-and-account/employees/services/employees.service"; -@ApiBearerAuth('access-token') -// @UseGuards() +@RolesAllowed(...GLOBAL_CONTROLLER_ROLES) @Controller('employees') export class EmployeesController { constructor( private readonly employeesService: EmployeesService, - private readonly archiveService: EmployeesArchivalService, - ) {} + private readonly archiveService: EmployeesArchivalService, + ) { } @Get('employee-list') - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ACCOUNTING) - + @RolesAllowed(...MANAGER_ROLES) findListEmployees(): Promise { return this.employeesService.findListEmployees(); } @Patch(':email') - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) - @ApiBearerAuth('access-token') + @RolesAllowed(...MANAGER_ROLES) async updateOrArchiveOrRestore(@Param('email') email: string, @Body() dto: UpdateEmployeeDto,) { // if last_work_day is set => archive the employee // else if employee is archived and first_work_day or last_work_day = null => restore //otherwise => standard update const result = await this.archiveService.patchEmployee(email, dto); - if(!result) { - throw new NotFoundException(`Employee with email: ${ email } is not found in active or archive.`) + if (!result) { + throw new NotFoundException(`Employee with email: ${email} is not found in active or archive.`) } return result; } @@ -68,10 +66,6 @@ export class EmployeesController { // } @Get('profile/:email') - @ApiOperation({summary: 'Find employee profile' }) - @ApiParam({ name: 'email', type: String, description: 'Identifier of the employee' }) - @ApiResponse({ status: 200, description: 'Employee profile found', type: EmployeeProfileItemDto }) - @ApiResponse({ status: 400, description: 'Employee profile not found' }) findOneProfile(@Param('email') email: string): Promise { return this.employeesService.findOneProfile(email); } diff --git a/src/identity-and-account/users-management/services/abstract-user.service.ts b/src/identity-and-account/users-management/services/abstract-user.service.ts index 4e0da2e..9a2ffd1 100644 --- a/src/identity-and-account/users-management/services/abstract-user.service.ts +++ b/src/identity-and-account/users-management/services/abstract-user.service.ts @@ -4,38 +4,38 @@ import { PrismaService } from 'src/prisma/prisma.service'; @Injectable() export abstract class AbstractUserService { - constructor(protected readonly prisma: PrismaService) {} + constructor(protected readonly prisma: PrismaService) { } - findAll(): Promise { - return this.prisma.users.findMany(); - } + findAll(): Promise { + return this.prisma.users.findMany(); + } - async findOne( id: string ): Promise { - const user = await this.prisma.users.findUnique({ where: { id } }); - if (!user) { - throw new NotFoundException(`User #${id} not found`); - } - return user; - } + async findOne(id: string): Promise { + const user = await this.prisma.users.findUnique({ where: { id } }); + if (!user) { + throw new NotFoundException(`User #${id} not found`); + } + return user; + } - async findOneByEmail( email: string ): Promise> { - const user = await this.prisma.users.findUnique({ where: { email } }); - if (!user) { - throw new NotFoundException(`No user with email #${email} exists`); - } + async findOneByEmail(email: string): Promise> { + const user = await this.prisma.users.findUnique({ where: { email } }); + if (!user) { + throw new NotFoundException(`No user with email #${email} exists`); + } - const clean_user = { - first_name: user.first_name, - last_name: user.last_name, - email: user.email, - role: user.role, - } - - return clean_user; - } + const clean_user = { + first_name: user.first_name, + last_name: user.last_name, + email: user.email, + role: user.role, + } - async remove(id: string): Promise { - await this.findOne(id); - return this.prisma.users.delete({ where: { id } }); - } + return clean_user; + } + + async remove(id: string): Promise { + await this.findOne(id); + return this.prisma.users.delete({ where: { id } }); + } } diff --git a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index 042b8df..d96cf17 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -6,53 +6,54 @@ import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracke import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; import { Roles as RoleEnum } from '.prisma/client'; +import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes"; @Controller('schedule-presets') +@RolesAllowed(...GLOBAL_CONTROLLER_ROLES) export class SchedulePresetsController { constructor( - private readonly upsertService: SchedulePresetsUpsertService, - private readonly getService: SchedulePresetsGetService, + private readonly upsertService: SchedulePresetsUpsertService, + private readonly getService: SchedulePresetsGetService, private readonly applyPresetsService: SchedulePresetsApplyService, - ){} + ) { } //used to create a schedule preset @Post('create') - @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) - async createPreset( @Req() req, @Body() dto: SchedulePresetsDto ) { + @RolesAllowed(...MANAGER_ROLES) + async createPreset(@Req() req, @Body() dto: SchedulePresetsDto) { const email = req.user?.email; return await this.upsertService.createPreset(email, dto); } //used to update an already existing schedule preset @Patch('update/:preset_id') - @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) - async updatePreset( @Param('preset_id', ParseIntPipe) preset_id: number,@Body() dto: SchedulePresetsUpdateDto ) { + @RolesAllowed(...MANAGER_ROLES) + async updatePreset(@Param('preset_id', ParseIntPipe) preset_id: number, @Body() dto: SchedulePresetsUpdateDto) { return await this.upsertService.updatePreset(preset_id, dto); } //used to delete a schedule preset @Delete('delete/:preset_id') @RolesAllowed(RoleEnum.ADMIN) - async deletePreset( @Param('preset_id') preset_id: number ) { + async deletePreset(@Param('preset_id') preset_id: number) { return await this.upsertService.deletePreset(preset_id); } //used to show the list of available schedule presets @Get('find-list') - @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) - async findListById( @Req() req) { + @RolesAllowed(...MANAGER_ROLES) + async findListById(@Req() req) { const email = req.user?.email; return this.getService.getSchedulePresets(email); } //used to apply a preset to a timesheet @Post('apply-presets') - @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) - async applyPresets( @Req() req, @Query('preset') preset_name: string, @Query('start') start_date: string ) { + async applyPresets(@Req() req, @Query('preset') preset_name: string, @Query('start') start_date: string) { const email = req.user?.email; - if(!preset_name?.trim()) throw new BadRequestException('Query "preset" is required'); - if(!start_date?.trim()) throw new BadRequestException('Query "start" is required YYYY-MM-DD'); + if (!preset_name?.trim()) throw new BadRequestException('Query "preset" is required'); + if (!start_date?.trim()) throw new BadRequestException('Query "start" is required YYYY-MM-DD'); return this.applyPresetsService.applyToTimesheet(email, preset_name, start_date); } } \ No newline at end of file diff --git a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts index b9ce63d..886536b 100644 --- a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts @@ -3,17 +3,18 @@ import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service"; import { CreateShiftResult, UpdateShiftResult } from "src/time-and-attendance/utils/type.utils"; -import { Roles as RoleEnum } from '.prisma/client'; import { RolesAllowed } from "src/common/decorators/roles.decorators"; +import { GLOBAL_CONTROLLER_ROLES } from "src/common/shared/role-groupes"; + @Controller('shift') +@RolesAllowed(...GLOBAL_CONTROLLER_ROLES) export class ShiftController { constructor( private readonly upsert_service: ShiftsUpsertService, ){} @Post('create') - @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) createBatch( @Req() req, @Body()dtos: ShiftDto[]): Promise { const email = req.user?.email; const list = Array.isArray(dtos) ? dtos : []; @@ -21,10 +22,7 @@ export class ShiftController { return this.upsert_service.createShifts(email, dtos) } - - //change Body to receive dtos @Patch('update') - @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) updateBatch( @Body() dtos: UpdateShiftDto[]): Promise{ const list = Array.isArray(dtos) ? dtos: []; if(list.length === 0) throw new BadRequestException('Body is missing or invalid (update shifts)'); @@ -32,9 +30,8 @@ export class ShiftController { } @Delete(':shift_id') - @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) remove(@Param('shift_id') shift_id: number ) { return this.upsert_service.deleteShift(shift_id); } - + } diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index e1d36f4..0a2e96c 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -41,16 +41,16 @@ export class ShiftsUpsertService { if (normed.end_time <= normed.start_time) { const error = { error_code: 'SHIFT_OVERLAP', - conflicts: { - start_time: toStringFromHHmm(normed.start_time), - end_time: toStringFromHHmm(normed.end_time), + conflicts: { + start_time: toStringFromHHmm(normed.start_time), + end_time: toStringFromHHmm(normed.end_time), date: toStringFromDate(normed.date), }, }; return { index, error }; } - if(!normed.end_time) throw new BadRequestException('A shift needs an end_time'); - if(!normed.start_time) throw new BadRequestException('A shift needs a start_time'); + if (!normed.end_time) throw new BadRequestException('A shift needs an end_time'); + if (!normed.start_time) throw new BadRequestException('A shift needs a start_time'); const timesheet = await this.prisma.timesheets.findUnique({ where: { id: dto.timesheet_id, employee_id }, @@ -59,9 +59,9 @@ export class ShiftsUpsertService { if (!timesheet) { const error = { error_code: 'INVALID_TIMESHEET', - conflicts: { - start_time: toStringFromHHmm(normed.start_time), - end_time: toStringFromHHmm(normed.end_time), + conflicts: { + start_time: toStringFromHHmm(normed.start_time), + end_time: toStringFromHHmm(normed.end_time), date: toStringFromDate(normed.date), }, }; @@ -116,14 +116,14 @@ export class ShiftsUpsertService { if ( overlaps( { start: ordered[j - 1].start, end: ordered[j - 1].end, date: ordered[j - 1].date }, - { start: ordered[j].start, end: ordered[j].end, date: ordered[j].date }, + { start: ordered[j].start, end: ordered[j].end, date: ordered[j].date }, ) ) { const error = new ConflictException({ error_code: 'SHIFT_OVERLAP', - conflicts: { - start_time: toStringFromHHmm(ordered[j].start), - end_time: toStringFromHHmm(ordered[j].end), + conflicts: { + start_time: toStringFromHHmm(ordered[j].start), + end_time: toStringFromHHmm(ordered[j].end), date: toStringFromDate(ordered[j].date), }, }); @@ -148,7 +148,7 @@ export class ShiftsUpsertService { where: { timesheet_id, date: day_date }, select: { start_time: true, end_time: true, id: true, date: true }, }); - existing_map.set( key, rows.map((row) => ({ start_time: row.start_time, end_time: row.end_time, date: row.date }))); + existing_map.set(key, rows.map((row) => ({ start_time: row.start_time, end_time: row.end_time, date: row.date }))); } normed_shifts.forEach((x, i) => { @@ -164,8 +164,8 @@ export class ShiftsUpsertService { existing = []; existing_map.set(map_key, existing); } - const hit = existing.find(exist => overlaps({ start: exist.start_time, end: exist.end_time, date: exist.date }, - { start: normed.start_time, end: normed.end_time, date:normed.date})); + const hit = existing.find(exist => overlaps({ start: exist.start_time, end: exist.end_time, date: exist.date }, + { start: normed.start_time, end: normed.end_time, date: normed.date })); if (hit) { results[index] = { ok: false, @@ -201,7 +201,7 @@ export class ShiftsUpsertService { }; existing.push(normalized_row); existing_map.set(map_key, existing); - + const { type: bank_type } = await this.typeResolver.findTypeByBankCodeId(row.bank_code_id); const summary = await this.overtime.getWeekOvertimeSummary(timesheet_id, normed.date, tx); @@ -236,11 +236,11 @@ export class ShiftsUpsertService { // recalculate overtime after update // return an updated version to display async updateShifts(dtos: UpdateShiftDto[]): Promise { - if (!Array.isArray(dtos) || dtos.length === 0) return []; + if (!Array.isArray(dtos) || dtos.length === 0) throw new BadRequestException({ error_code: 'SHIFT_MISSING' }); const updates: UpdateShiftPayload[] = await Promise.all(dtos.map((item) => { const { id, ...rest } = item; - if (!Number.isInteger(id)) throw new ConflictException({ error_code: 'INVALID_SHIFT'}); + if (!id) throw new BadRequestException({ error_code: 'SHIFT_INVALID' }); const changes: UpdateShiftChanges = {}; if (rest.date !== undefined) changes.date = rest.date; @@ -265,13 +265,15 @@ export class ShiftsUpsertService { const existing = regroup_id.get(update.id); if (!existing) { return updates.map(exist => exist.id === update.id - ? ({ ok: false, id: update.id, error: new NotFoundException(`Shift with id: ${update.id} not found`) } as UpdateShiftResult) - : ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to missing shift') })); + ? ({ ok: false, id: update.id, error: new NotFoundException({ error_code: 'SHIFT_MISSING' }) } as UpdateShiftResult) + : ({ ok: false, id: exist.id, error: new BadRequestException({ error_code: 'SHIFT_INVALID' }) }) + ); } if (existing.is_approved) { return updates.map(exist => exist.id === update.id - ? ({ ok: false, id: update.id, error: new BadRequestException('Approved shift cannot be updated') } as UpdateShiftResult) - : ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to approved shift in update set') })); + ? ({ ok: false, id: update.id, error: new BadRequestException({ error_code: 'SHIFT_INVALID' }) } as UpdateShiftResult) + : ({ ok: false, id: exist.id, error: new BadRequestException({ error_code: 'SHIFT_INVALID' }) }) + ); } } @@ -307,12 +309,14 @@ export class ShiftsUpsertService { where: { timesheet_id: group.timesheet_id, date: day_date }, select: { id: true, start_time: true, end_time: true, date: true }, }); - groups.set(key(group.timesheet_id, day_date), { existing: existing.map(row => ({ - id: row.id, - start: row.start_time, - end: row.end_time, - date: row.date, - })), incoming: planned_updates }); + groups.set(key(group.timesheet_id, day_date), { + existing: existing.map(row => ({ + id: row.id, + start: row.start_time, + end: row.end_time, + date: row.date, + })), incoming: planned_updates + }); } for (const planned of planned_updates) { @@ -320,23 +324,23 @@ export class ShiftsUpsertService { const group = groups.get(keys)!; const conflict = group.existing.find(row => - row.id !== planned.exist_shift.id && overlaps({ start: row.start, end: row.end, date: row.date }, + row.id !== planned.exist_shift.id && overlaps({ start: row.start, end: row.end, date: row.date }, { start: planned.normed.start_time, end: planned.normed.end_time, date: planned.normed.date }) ); if (conflict) { return updates.map(exist => exist.id === planned.exist_shift.id ? ({ - ok: false, id: exist.id, error:{ + ok: false, id: exist.id, error: { error_code: 'SHIFT_OVERLAP', - conflicts: { - start_time: toStringFromHHmm(conflict.start), - end_time: toStringFromHHmm(conflict.end), + conflicts: { + start_time: toStringFromHHmm(conflict.start), + end_time: toStringFromHHmm(conflict.end), date: toStringFromDate(conflict.date), }, } } as UpdateShiftResult) - : ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to overlap in another update') }) + : ({ ok: false, id: exist.id, error: new BadRequestException({ error_code: 'SHIFT_OVERLAP' }) }) ); } } @@ -345,29 +349,29 @@ export class ShiftsUpsertService { for (const planned of planned_updates) { const keys = key(planned.exist_shift.timesheet_id, planned.normed.date); if (!regoup_by_day.has(keys)) regoup_by_day.set(keys, []); - regoup_by_day.get(keys)!.push({ - id: planned.exist_shift.id, - start: planned.normed.start_time, - end: planned.normed.end_time, - date: planned.normed.date + regoup_by_day.get(keys)!.push({ + id: planned.exist_shift.id, + start: planned.normed.start_time, + end: planned.normed.end_time, + date: planned.normed.date }); } - + for (const arr of regoup_by_day.values()) { arr.sort((a, b) => a.start.getTime() - b.start.getTime()); for (let i = 1; i < arr.length; i++) { if (overlaps( - { start: arr[i - 1].start, end: arr[i - 1].end, date: arr[i - 1].date }, - { start: arr[i].start, end: arr[i].end, date: arr[i].date }) + { start: arr[i - 1].start, end: arr[i - 1].end, date: arr[i - 1].date }, + { start: arr[i].start, end: arr[i].end, date: arr[i].date }) ) { - const error = { + const error = { error_code: 'SHIFT_OVERLAP', - conflicts: { - start_time: toStringFromHHmm(arr[i].start), - end_time: toStringFromHHmm(arr[i].end), + conflicts: { + start_time: toStringFromHHmm(arr[i].start), + end_time: toStringFromHHmm(arr[i].end), date: toStringFromDate(arr[i].date), }, - + }; return updates.map(exist => ({ ok: false, id: exist.id, error: error })); } @@ -426,7 +430,7 @@ export class ShiftsUpsertService { where: { id: shift_id }, select: { id: true, date: true, timesheet_id: true }, }); - if (!shift) throw new ConflictException({ error_code: 'INVALID_SHIFT'}); + if (!shift) throw new ConflictException({ error_code: 'SHIFT_INVALID' }); await tx.shifts.delete({ where: { id: shift_id } }); diff --git a/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts index 849a121..96bb419 100644 --- a/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts @@ -1,31 +1,34 @@ -import { Body, Controller, Get, ParseBoolPipe, ParseIntPipe, Patch, Query, Req, UnauthorizedException} from "@nestjs/common"; +import { Body, Controller, Get, ParseBoolPipe, ParseIntPipe, Patch, Query, Req, UnauthorizedException } from "@nestjs/common"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service"; -import { Roles as RoleEnum } from '.prisma/client'; import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service"; +import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes"; @Controller('timesheets') +@RolesAllowed(...GLOBAL_CONTROLLER_ROLES) export class TimesheetController { - constructor( + constructor( private readonly timesheetOverview: GetTimesheetsOverviewService, private readonly approvalService: TimesheetApprovalService, - ){} + ) { } @Get() - @RolesAllowed(RoleEnum.SUPERVISOR, RoleEnum.HR, RoleEnum.ACCOUNTING, RoleEnum.ADMIN) - async getTimesheetByIds( - @Req() req, @Query('year', ParseIntPipe) year:number, @Query('period_number', ParseIntPipe) period_number: number) { + getTimesheetByPayPeriod( + @Req() req, + @Query('year', ParseIntPipe) year: number, + @Query('period_number', ParseIntPipe) period_number: number + ) { const email = req.user?.email; - if(!email) throw new UnauthorizedException('Unauthorized User'); + if (!email) throw new UnauthorizedException('Unauthorized User'); return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(email, year, period_number); } @Patch('timesheet-approval') - @RolesAllowed(RoleEnum.SUPERVISOR, RoleEnum.HR, RoleEnum.ACCOUNTING, RoleEnum.ADMIN) - async approveTimesheet( + @RolesAllowed(...MANAGER_ROLES) + approveTimesheet( @Body('timesheet_id', ParseIntPipe) timesheet_id: number, - @Body('is_approved' , ParseBoolPipe) is_approved: boolean, + @Body('is_approved', ParseBoolPipe) is_approved: boolean, ) { return this.approvalService.approveTimesheetById(timesheet_id, is_approved); } From 88d4f2fe272c74a17d2441e45cbcf4785a099a2d Mon Sep 17 00:00:00 2001 From: Nicolas Drolet Date: Wed, 5 Nov 2025 14:31:31 -0500 Subject: [PATCH 47/57] fix(shift): fix minor issue with naming property (changed to shift_id from id) --- src/main.ts | 4 ++- .../shifts/dtos/shift-update.dto.ts | 2 +- .../shifts/services/shifts-upsert.service.ts | 33 ++++++++++--------- src/time-and-attendance/utils/type.utils.ts | 4 +-- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/main.ts b/src/main.ts index 7c06222..fbe5afd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -19,6 +19,8 @@ import { writeFileSync } from 'fs'; import * as session from 'express-session'; import * as passport from 'passport'; +const SESSION_TOKEN_DURATION_MINUTES = 180 + async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -37,7 +39,7 @@ async function bootstrap() { saveUninitialized: false, rolling: true, cookie: { - maxAge: 30 * 60 * 1000, + maxAge: SESSION_TOKEN_DURATION_MINUTES * 60 * 1000, // property maxAge requires milliseconds httpOnly: true, } })) diff --git a/src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto.ts b/src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto.ts index 0dd4505..aef90e3 100644 --- a/src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto.ts +++ b/src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto.ts @@ -7,5 +7,5 @@ export class UpdateShiftDto extends PartialType( OmitType(ShiftDto, ['is_approved', 'timesheet_id'] as const), ) { @IsInt() - id!: number; + shift_id!: number; } diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index e1d36f4..77e34f8 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -239,8 +239,9 @@ export class ShiftsUpsertService { if (!Array.isArray(dtos) || dtos.length === 0) return []; const updates: UpdateShiftPayload[] = await Promise.all(dtos.map((item) => { - const { id, ...rest } = item; - if (!Number.isInteger(id)) throw new ConflictException({ error_code: 'INVALID_SHIFT'}); + const { shift_id, ...rest } = item; + console.log('id received: ', shift_id); + if (!Number.isInteger(shift_id)) throw new ConflictException({ error_code: 'INVALID_SHIFT'}); const changes: UpdateShiftChanges = {}; if (rest.date !== undefined) changes.date = rest.date; @@ -250,11 +251,11 @@ export class ShiftsUpsertService { if (rest.is_remote !== undefined) changes.is_remote = rest.is_remote; if (rest.comment !== undefined) changes.comment = rest.comment; - return { id, dto: changes }; + return { shift_id, dto: changes }; })); return this.prisma.$transaction(async (tx) => { - const shift_ids = updates.map(update_shift => update_shift.id); + const shift_ids = updates.map(update_shift => update_shift.shift_id); const rows = await tx.shifts.findMany({ where: { id: { in: shift_ids } }, select: shift_select, @@ -262,21 +263,21 @@ export class ShiftsUpsertService { const regroup_id = new Map(rows.map(r => [r.id, r])); for (const update of updates) { - const existing = regroup_id.get(update.id); + const existing = regroup_id.get(update.shift_id); if (!existing) { - return updates.map(exist => exist.id === update.id - ? ({ ok: false, id: update.id, error: new NotFoundException(`Shift with id: ${update.id} not found`) } as UpdateShiftResult) - : ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to missing shift') })); + return updates.map(exist => exist.shift_id === update.shift_id + ? ({ ok: false, id: update.shift_id, error: new NotFoundException(`Shift with id: ${update.shift_id} not found`) } as UpdateShiftResult) + : ({ ok: false, id: exist.shift_id, error: new BadRequestException('Batch aborted due to missing shift') })); } if (existing.is_approved) { - return updates.map(exist => exist.id === update.id - ? ({ ok: false, id: update.id, error: new BadRequestException('Approved shift cannot be updated') } as UpdateShiftResult) - : ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to approved shift in update set') })); + return updates.map(exist => exist.shift_id === update.shift_id + ? ({ ok: false, id: update.shift_id, error: new BadRequestException('Approved shift cannot be updated') } as UpdateShiftResult) + : ({ ok: false, id: exist.shift_id, error: new BadRequestException('Batch aborted due to approved shift in update set') })); } } const planned_updates = updates.map(update => { - const exist_shift = regroup_id.get(update.id)!; + const exist_shift = regroup_id.get(update.shift_id)!; const date_string = update.dto.date ?? toStringFromDate(exist_shift.date); const start_string = update.dto.start_time ?? toStringFromHHmm(exist_shift.start_time); const end_string = update.dto.end_time ?? toStringFromHHmm(exist_shift.end_time); @@ -325,9 +326,9 @@ export class ShiftsUpsertService { ); if (conflict) { return updates.map(exist => - exist.id === planned.exist_shift.id + exist.shift_id === planned.exist_shift.id ? ({ - ok: false, id: exist.id, error:{ + ok: false, id: exist.shift_id, error:{ error_code: 'SHIFT_OVERLAP', conflicts: { start_time: toStringFromHHmm(conflict.start), @@ -336,7 +337,7 @@ export class ShiftsUpsertService { }, } } as UpdateShiftResult) - : ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to overlap in another update') }) + : ({ ok: false, id: exist.shift_id, error: new BadRequestException('Batch aborted due to overlap in another update') }) ); } } @@ -369,7 +370,7 @@ export class ShiftsUpsertService { }, }; - return updates.map(exist => ({ ok: false, id: exist.id, error: error })); + return updates.map(exist => ({ ok: false, id: exist.shift_id, error: error })); } } } diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts index c9613bb..a4844bf 100644 --- a/src/time-and-attendance/utils/type.utils.ts +++ b/src/time-and-attendance/utils/type.utils.ts @@ -33,8 +33,8 @@ export type ShiftWithOvertimeDto = { }; export type CreateShiftResult = { ok: true; data: ShiftWithOvertimeDto } | { ok: false; error: any }; -export type UpdateShiftChanges = Omit; -export type UpdateShiftPayload = { id: number; dto: UpdateShiftChanges }; +export type UpdateShiftChanges = Omit; +export type UpdateShiftPayload = { shift_id: number; dto: UpdateShiftChanges }; export type UpdateShiftResult = { ok: true; id: number; data: ShiftWithOvertimeDto } | { ok: false; id: number; error: any }; export type DeleteShiftResult = { ok: true; id: number; overtime: WeekOvertimeSummary } | { ok: false; id: number; error: any }; From c0189dc61d601a10e3d2466ceffe47b9bbe4eab3 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Thu, 6 Nov 2025 11:04:55 -0500 Subject: [PATCH 48/57] fix(shifts): rework update and create to match ShiftEntity --- .../controllers/employees.controller.ts | 30 +-- .../shifts/controllers/shift.controller.ts | 2 +- .../time-tracker/shifts/dtos/shift-get.dto.ts | 1 + .../shifts/dtos/shift-payload.dto.ts | 11 + .../shifts/services/shifts-upsert.service.ts | 242 +++++++++--------- src/time-and-attendance/utils/type.utils.ts | 5 +- 6 files changed, 149 insertions(+), 142 deletions(-) create mode 100644 src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto.ts diff --git a/src/identity-and-account/employees/controllers/employees.controller.ts b/src/identity-and-account/employees/controllers/employees.controller.ts index fa45089..8b58d4a 100644 --- a/src/identity-and-account/employees/controllers/employees.controller.ts +++ b/src/identity-and-account/employees/controllers/employees.controller.ts @@ -15,6 +15,11 @@ export class EmployeesController { private readonly archiveService: EmployeesArchivalService, ) { } + @Get('profile/:email') + findOneProfile(@Param('email') email: string): Promise { + return this.employeesService.findOneProfile(email); + } + @Get('employee-list') @RolesAllowed(...MANAGER_ROLES) findListEmployees(): Promise { @@ -34,6 +39,8 @@ export class EmployeesController { return result; } + + //_____________________________________________________________________________________________ // Deprecated or unused methods //_____________________________________________________________________________________________ @@ -46,29 +53,6 @@ export class EmployeesController { // create(@Body() dto: CreateEmployeeDto): Promise { // return this.employeesService.create(dto); // } - // @Get() - // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ACCOUNTING) - // @ApiOperation({summary: 'Find all employees' }) - // @ApiResponse({ status: 200, description: 'List of employees found', type: CreateEmployeeDto, isArray: true }) - // @ApiResponse({ status: 400, description: 'List of employees not found' }) - // findAll(): Promise { - // return this.employeesService.findAll(); - // } - - - // @Get(':email') - // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR,RoleEnum.ACCOUNTING ) - // @ApiOperation({summary: 'Find employee' }) - // @ApiResponse({ status: 200, description: 'Employee found', type: CreateEmployeeDto }) - // @ApiResponse({ status: 400, description: 'Employee not found' }) - // findOne(@Param('email', ParseIntPipe) email: string): Promise { - // return this.employeesService.findOne(email); - // } - - @Get('profile/:email') - findOneProfile(@Param('email') email: string): Promise { - return this.employeesService.findOneProfile(email); - } // @Delete(':email') // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR ) diff --git a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts index 886536b..70c3034 100644 --- a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts @@ -23,7 +23,7 @@ export class ShiftController { } @Patch('update') - updateBatch( @Body() dtos: UpdateShiftDto[]): Promise{ + updateBatch( @Body() dtos: ShiftDto[]): Promise{ const list = Array.isArray(dtos) ? dtos: []; if(list.length === 0) throw new BadRequestException('Body is missing or invalid (update shifts)'); return this.upsert_service.updateShifts(dtos); diff --git a/src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto.ts b/src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto.ts index 969ca98..c5fd877 100644 --- a/src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto.ts +++ b/src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto.ts @@ -1,4 +1,5 @@ export class GetShiftDto { + shift_id: number; timesheet_id: number; type: string; date: string; diff --git a/src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto.ts b/src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto.ts new file mode 100644 index 0000000..ce2363b --- /dev/null +++ b/src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto.ts @@ -0,0 +1,11 @@ +export class ShiftEntity { + id: number; + timesheet_id: number; + bank_code_id: number; + date: string; + start_time: string; + end_time: string; + is_remote: boolean; + is_approved: boolean; + comment?: string; +} diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 12705a1..d02bed5 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -1,16 +1,15 @@ -import { CreateShiftResult, NormedOk, UpdateShiftResult, UpdateShiftPayload, UpdateShiftChanges, Normalized } from "src/time-and-attendance/utils/type.utils"; +import { CreateShiftResult, NormedOk, UpdateShiftResult, Normalized } from "src/time-and-attendance/utils/type.utils"; import { overlaps, toStringFromHHmm, toStringFromDate, toDateFromString, toHHmmFromString } from "src/time-and-attendance/utils/date-time.utils"; import { Injectable, BadRequestException, ConflictException, NotFoundException } from "@nestjs/common"; import { shift_select, timesheet_select } from "src/time-and-attendance/utils/selects.utils"; import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; -import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; import { PrismaService } from "src/prisma/prisma.service"; import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; +import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto"; import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; - - +import { response } from "express"; @Injectable() export class ShiftsUpsertService { @@ -33,54 +32,59 @@ export class ShiftsUpsertService { if (!Array.isArray(dtos) || dtos.length === 0) return []; const employee_id = await this.emailResolver.findIdByEmail(email); - - const normed_shifts = await Promise.all( - dtos.map(async (dto, index) => { - try { - const normed = await this.normalizeShiftDto(dto); - if (normed.end_time <= normed.start_time) { - const error = { - error_code: 'SHIFT_OVERLAP', - conflicts: { - start_time: toStringFromHHmm(normed.start_time), - end_time: toStringFromHHmm(normed.end_time), - date: toStringFromDate(normed.date), - }, - }; - return { index, error }; - } - if (!normed.end_time) throw new BadRequestException('A shift needs an end_time'); - if (!normed.start_time) throw new BadRequestException('A shift needs a start_time'); - - const timesheet = await this.prisma.timesheets.findUnique({ - where: { id: dto.timesheet_id, employee_id }, - select: timesheet_select, - }); - if (!timesheet) { - const error = { - error_code: 'INVALID_TIMESHEET', - conflicts: { - start_time: toStringFromHHmm(normed.start_time), - end_time: toStringFromHHmm(normed.end_time), - date: toStringFromDate(normed.date), - }, - }; - return { index, error }; - } - - return { - index, - dto, - normed, - timesheet_id: timesheet.id, + const results: CreateShiftResult[] = []; + const normed_shifts: (NormedOk | undefined)[] = await Promise.all(dtos.map(async (dto, index) => { + try { + const normed = await this.normalizeShiftDto(dto); + if (normed.end_time <= normed.start_time) { + const error = { + error_code: 'SHIFT_OVERLAP', + conflicts: { + start_time: toStringFromHHmm(normed.start_time), + end_time: toStringFromHHmm(normed.end_time), + date: toStringFromDate(normed.date), + }, }; - } catch (error) { - return { index, error }; + results.push({ ok: false, error }); } - })); - const ok_items = normed_shifts.filter( - (item): item is NormedOk & { timesheet_id: number } => "normed" in item); + const timesheet = await this.prisma.timesheets.findUnique({ + where: { id: dto.timesheet_id, employee_id }, + select: timesheet_select, + }); + if (!timesheet) { + const error = { + error_code: 'INVALID_TIMESHEET', + conflicts: { + start_time: toStringFromHHmm(normed.start_time), + end_time: toStringFromHHmm(normed.end_time), + date: toStringFromDate(normed.date), + }, + }; + results.push({ ok: false, error }); + return; + } + const bank_code = await this.typeResolver.findBankCodeIDByType(dto.type); + const entity: ShiftEntity = { + bank_code_id: bank_code.id, + ...dto, + }; + + return { + index, + dto: entity, + normed, + timesheet_id: timesheet.id, + }; + } catch (error) { + results.push({ ok: false, error }); + return; + } + + })); + + const ok_items = normed_shifts.filter((item) => item !== undefined); + const regroup_by_date = new Map(); ok_items.forEach(({ index, normed, timesheet_id }) => { @@ -151,7 +155,7 @@ export class ShiftsUpsertService { existing_map.set(key, rows.map((row) => ({ start_time: row.start_time, end_time: row.end_time, date: row.date }))); } - normed_shifts.forEach((x, i) => { + ok_items.forEach((x, i) => { if ("error" in x) results[i] = { ok: false, error: x.error }; }); @@ -184,7 +188,7 @@ export class ShiftsUpsertService { const row = await tx.shifts.create({ data: { timesheet_id: timesheet_id, - bank_code_id: normed.id, + bank_code_id: normed.bank_code_id, date: normed.date, start_time: normed.start_time, end_time: normed.end_time, @@ -207,6 +211,7 @@ export class ShiftsUpsertService { const summary = await this.overtime.getWeekOvertimeSummary(timesheet_id, normed.date, tx); const shift: GetShiftDto = { + shift_id: row.id, timesheet_id: timesheet_id, type: bank_type, date: toStringFromDate(row.date), @@ -235,26 +240,24 @@ export class ShiftsUpsertService { // update shifts in DB // recalculate overtime after update // return an updated version to display - async updateShifts(dtos: UpdateShiftDto[]): Promise { + async updateShifts(dtos: ShiftDto[]): Promise { if (!Array.isArray(dtos) || dtos.length === 0) throw new BadRequestException({ error_code: 'SHIFT_MISSING' }); - const updates: UpdateShiftPayload[] = await Promise.all(dtos.map((item) => { - const { shift_id, ...rest } = item; - if (!shift_id) throw new BadRequestException({ error_code: 'SHIFT_INVALID' }); - - const changes: UpdateShiftChanges = {}; - if (rest.date !== undefined) changes.date = rest.date; - if (rest.start_time !== undefined) changes.start_time = rest.start_time; - if (rest.end_time !== undefined) changes.end_time = rest.end_time; - if (rest.type !== undefined) changes.type = rest.type; - if (rest.is_remote !== undefined) changes.is_remote = rest.is_remote; - if (rest.comment !== undefined) changes.comment = rest.comment; - - return { shift_id, dto: changes }; + const updates: ShiftEntity[] = await Promise.all(dtos.map(async (item) => { + try { + const bank_code = await this.typeResolver.findBankCodeIDByType(item.type); + return { + bank_code_id: bank_code.id, + ...item, + } + } catch (error) { + throw new BadRequestException('INVALID_SHIFT'); + } })); return this.prisma.$transaction(async (tx) => { - const shift_ids = updates.map(update_shift => update_shift.shift_id); + + const shift_ids = updates.map(update_shift => update_shift.id); const rows = await tx.shifts.findMany({ where: { id: { in: shift_ids } }, select: shift_select, @@ -262,31 +265,28 @@ export class ShiftsUpsertService { const regroup_id = new Map(rows.map(r => [r.id, r])); for (const update of updates) { - const existing = regroup_id.get(update.shift_id); + const existing = regroup_id.get(update.id); if (!existing) { - return updates.map(exist => exist.shift_id === update.shift_id - ? ({ ok: false, id: update.shift_id, error: new NotFoundException({ error_code: 'SHIFT_MISSING' }) } as UpdateShiftResult) - : ({ ok: false, id: exist.shift_id, error: new BadRequestException({ error_code: 'SHIFT_INVALID' }) }) + return updates.map(exist => exist.id === update.id + ? ({ ok: false, id: update.id, error: new NotFoundException({ error_code: 'SHIFT_MISSING' }) } as UpdateShiftResult) + : ({ ok: false, id: exist.id, error: new BadRequestException({ error_code: 'SHIFT_INVALID' }) }) ); } if (existing.is_approved) { - return updates.map(exist => exist.shift_id === update.shift_id - ? ({ ok: false, id: update.shift_id, error: new BadRequestException({ error_code: 'SHIFT_INVALID' }) } as UpdateShiftResult) - : ({ ok: false, id: exist.shift_id, error: new BadRequestException({ error_code: 'SHIFT_INVALID' }) }) + return updates.map(exist => exist.id === update.id + ? ({ ok: false, id: update.id, error: new BadRequestException({ error_code: 'SHIFT_INVALID' }) } as UpdateShiftResult) + : ({ ok: false, id: exist.id, error: new BadRequestException({ error_code: 'SHIFT_INVALID' }) }) ); } } const planned_updates = updates.map(update => { - const exist_shift = regroup_id.get(update.shift_id)!; - const date_string = update.dto.date ?? toStringFromDate(exist_shift.date); - const start_string = update.dto.start_time ?? toStringFromHHmm(exist_shift.start_time); - const end_string = update.dto.end_time ?? toStringFromHHmm(exist_shift.end_time); + const exist_shift = regroup_id.get(update.id)!; const normed: Normalized = { - date: toDateFromString(date_string), - start_time: toHHmmFromString(start_string), - end_time: toHHmmFromString(end_string), - id: exist_shift.id, + date: toDateFromString(update.date), + start_time: toHHmmFromString(update.start_time), + end_time: toHHmmFromString(update.end_time), + bank_code_id: exist_shift.bank_code_id, }; return { update, exist_shift, normed }; }); @@ -329,9 +329,9 @@ export class ShiftsUpsertService { ); if (conflict) { return updates.map(exist => - exist.shift_id === planned.exist_shift.id + exist.id === planned.exist_shift.id ? ({ - ok: false, id: exist.shift_id, error:{ + ok: false, id: exist.id, error: { error_code: 'SHIFT_OVERLAP', conflicts: { start_time: toStringFromHHmm(conflict.start), @@ -340,7 +340,7 @@ export class ShiftsUpsertService { }, } } as UpdateShiftResult) - : ({ ok: false, id: exist.shift_id, error: new BadRequestException('Batch aborted due to overlap in another update') }) + : ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to overlap in another update') }) ); } } @@ -373,49 +373,59 @@ export class ShiftsUpsertService { }, }; - return updates.map(exist => ({ ok: false, id: exist.shift_id, error: error })); + return updates.map(exist => ({ ok: false, id: exist.id, error: error })); } } } const results: UpdateShiftResult[] = []; for (const planned of planned_updates) { - const data: any = {}; - const { dto } = planned.update; - if (dto.date !== undefined) data.date = planned.normed.date; - if (dto.start_time !== undefined) data.start_time = planned.normed.start_time; - if (dto.end_time !== undefined) data.end_time = planned.normed.end_time; - if (dto.type !== undefined) data.type = dto.type; - if (dto.is_remote !== undefined) data.is_remote = dto.is_remote; - if (dto.comment !== undefined) data.comment = dto.comment ?? null; + try { + const date = toStringFromDate(planned.normed.date); + const start_time = toStringFromHHmm(planned.normed.start_time); + const end_time = toStringFromHHmm(planned.normed.end_time); - const row = await tx.shifts.update({ - where: { id: planned.exist_shift.id }, - data, - select: shift_select, - }); + const data: Partial = { + bank_code_id: planned.normed.bank_code_id, + date: date, + start_time: start_time, + end_time: end_time, + is_remote: planned.update.is_remote, + is_approved: planned.exist_shift.is_approved, + comment: planned.update.comment, + }; - const summary_new = await this.overtime.getWeekOvertimeSummary(row.timesheet_id, planned.exist_shift.date, tx); - if (row.date.getTime() !== planned.exist_shift.date.getTime()) { - await this.overtime.getWeekOvertimeSummary(row.timesheet_id, row.date, tx); + const row = await tx.shifts.update({ + where: { id: planned.exist_shift.id }, + data, + select: shift_select, + }); + const summary_new = await this.overtime.getWeekOvertimeSummary(row.timesheet_id, planned.exist_shift.date, tx); + if (row.date.getTime() !== planned.exist_shift.date.getTime()) { + await this.overtime.getWeekOvertimeSummary(row.timesheet_id, row.date, tx); + } + + const type = await this.typeResolver.findTypeByBankCodeId(row.bank_code_id); + + const dto: GetShiftDto = { + shift_id: row.id, + timesheet_id: row.timesheet_id, + type: type.type, + date: toStringFromDate(row.date), + start_time: toStringFromHHmm(row.start_time), + end_time: toStringFromHHmm(row.end_time), + is_approved: row.is_approved, + is_remote: row.is_remote, + comment: row.comment ?? undefined, + }; + + results.push({ ok: true, id: planned.exist_shift.id, data: { shift: dto, overtime: summary_new } }); + } catch (error) { + throw new BadRequestException('INVALID_SHIFT'); } - - const shift: GetShiftDto = { - timesheet_id: row.timesheet_id, - type: data.type, - date: toStringFromDate(row.date), - start_time: toStringFromHHmm(row.start_time), - end_time: toStringFromHHmm(row.end_time), - is_approved: row.is_approved, - is_remote: row.is_remote, - comment: row.comment ?? undefined, - }; - - results.push({ ok: true, id: planned.exist_shift.id, data: { shift, overtime: summary_new } }); } return results; }); - } //_________________________________________________________________ @@ -451,6 +461,6 @@ export class ShiftsUpsertService { const date = toDateFromString(dto.date); const start_time = toHHmmFromString(dto.start_time); const end_time = toHHmmFromString(dto.end_time); - return { date, start_time, end_time, id: bank_code_id }; + return { date, start_time, end_time, bank_code_id: bank_code_id }; } } diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts index a4844bf..140ee97 100644 --- a/src/time-and-attendance/utils/type.utils.ts +++ b/src/time-and-attendance/utils/type.utils.ts @@ -4,6 +4,7 @@ import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense- import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; +import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto"; import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; @@ -25,7 +26,7 @@ export type TotalExpenses = { mileage: number; }; -export type Normalized = { date: Date; start_time: Date; end_time: Date; id: number}; +export type Normalized = { date: Date; start_time: Date; end_time: Date; bank_code_id: number}; export type ShiftWithOvertimeDto = { shift: GetShiftDto; @@ -51,7 +52,7 @@ export type DeleteExpenseResult = { ok: true; id: number; } | { ok: false; id: n -export type NormedOk = { index: number; dto: ShiftDto; normed: Normalized }; +export type NormedOk = { index: number; dto: ShiftEntity; normed: Normalized, timesheet_id: number }; export type NormedErr = { index: number; error: any }; From 4e48d98c0f23ed8e6d1c484eb5facfc777cae8a1 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Thu, 6 Nov 2025 11:10:25 -0500 Subject: [PATCH 49/57] fix(shifts): commented unused service function --- docs/swagger/swagger-spec.json | 2 +- .../shifts/services/shifts-get.service.ts | 52 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 4aad9e8..0f24da6 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -250,7 +250,7 @@ }, "/timesheets": { "get": { - "operationId": "TimesheetController_getTimesheetByIds", + "operationId": "TimesheetController_getTimesheetByPayPeriod", "parameters": [ { "name": "year", diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts index fb3dc48..96ae516 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts @@ -24,37 +24,37 @@ export class ShiftsGetService { ){} //fetch a shift using shift_id and return all that shift's info - async getShiftByShiftId(shift_ids: number[]): Promise { - if(!Array.isArray(shift_ids) || shift_ids.length === 0) return []; + // async getShiftByShiftId(shift_ids: number[]): Promise { + // if(!Array.isArray(shift_ids) || shift_ids.length === 0) return []; - const rows = await this.prisma.shifts.findMany({ - where: { id: { in: shift_ids } }, - select: shift_select, - }); + // const rows = await this.prisma.shifts.findMany({ + // where: { id: { in: shift_ids } }, + // select: shift_select, + // }); - if(rows.length !== shift_ids.length) { - const found_ids = new Set(rows.map(row => row.id)); - const missing_ids = shift_ids.filter(id => !found_ids.has(id)); - throw new NotFoundException(`Shift(s) not found: ${ missing_ids.join(", ")}`); - } + // if(rows.length !== shift_ids.length) { + // const found_ids = new Set(rows.map(row => row.id)); + // const missing_ids = shift_ids.filter(id => !found_ids.has(id)); + // throw new NotFoundException(`Shift(s) not found: ${ missing_ids.join(", ")}`); + // } - const row_by_id = new Map(rows.map(row => [row.id, row])); + // const row_by_id = new Map(rows.map(row => [row.id, row])); - return shift_ids.map((id) => { - const shift = row_by_id.get(id)!; - return { - timesheet_id: shift.timesheet_id, - type: shift.bank_code.type, - date: toStringFromDate(shift.date), - start_time: toStringFromHHmm(shift.start_time), - end_time: toStringFromHHmm(shift.end_time), - is_remote: shift.is_remote, - is_approved: shift.is_approved, - comment: shift.comment ?? undefined, - } satisfies GetShiftDto; - }); + // return shift_ids.map((id) => { + // const shift = row_by_id.get(id)!; + // return { + // timesheet_id: shift.timesheet_id, + // type: shift.bank_code.type, + // date: toStringFromDate(shift.date), + // start_time: toStringFromHHmm(shift.start_time), + // end_time: toStringFromHHmm(shift.end_time), + // is_remote: shift.is_remote, + // is_approved: shift.is_approved, + // comment: shift.comment ?? undefined, + // } satisfies GetShiftDto; + // }); - } + // } } \ No newline at end of file From e34658fc41c24cae5f814300b895c402fa6312bf Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Thu, 6 Nov 2025 12:47:36 -0500 Subject: [PATCH 50/57] fix(shifts): ajusted return --- .../shifts/dtos/shift-payload.dto.ts | 6 +-- .../shifts/services/shifts-upsert.service.ts | 38 +++++++++++++------ .../timesheet-get-overview.service.ts | 2 +- src/time-and-attendance/utils/type.utils.ts | 1 - 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto.ts b/src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto.ts index ce2363b..f2b833f 100644 --- a/src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto.ts +++ b/src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto.ts @@ -2,9 +2,9 @@ export class ShiftEntity { id: number; timesheet_id: number; bank_code_id: number; - date: string; - start_time: string; - end_time: string; + date: Date; + start_time: Date; + end_time: Date; is_remote: boolean; is_approved: boolean; comment?: string; diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index d02bed5..952ebc4 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -65,9 +65,18 @@ export class ShiftsUpsertService { return; } const bank_code = await this.typeResolver.findBankCodeIDByType(dto.type); + const date = await toDateFromString(dto.date); + const start_time = await toHHmmFromString(dto.start_time); + const end_time = await toHHmmFromString(dto.end_time); const entity: ShiftEntity = { + timesheet_id: timesheet.id, bank_code_id: bank_code.id, - ...dto, + date, + start_time, + end_time, + id: dto.id, + is_approved: dto.is_approved, + is_remote: dto.is_remote, }; return { @@ -245,10 +254,19 @@ export class ShiftsUpsertService { const updates: ShiftEntity[] = await Promise.all(dtos.map(async (item) => { try { + const date = await toDateFromString(item.date); + const start_time = await toHHmmFromString(item.start_time); + const end_time = await toHHmmFromString(item.end_time); const bank_code = await this.typeResolver.findBankCodeIDByType(item.type); return { + id: item.id, + timesheet_id: item.timesheet_id, bank_code_id: bank_code.id, - ...item, + date, + start_time, + end_time, + is_remote: item.is_remote, + is_approved: item.is_approved, } } catch (error) { throw new BadRequestException('INVALID_SHIFT'); @@ -283,9 +301,9 @@ export class ShiftsUpsertService { const planned_updates = updates.map(update => { const exist_shift = regroup_id.get(update.id)!; const normed: Normalized = { - date: toDateFromString(update.date), - start_time: toHHmmFromString(update.start_time), - end_time: toHHmmFromString(update.end_time), + date: update.date, + start_time: update.start_time, + end_time: update.end_time, bank_code_id: exist_shift.bank_code_id, }; return { update, exist_shift, normed }; @@ -381,15 +399,11 @@ export class ShiftsUpsertService { const results: UpdateShiftResult[] = []; for (const planned of planned_updates) { try { - const date = toStringFromDate(planned.normed.date); - const start_time = toStringFromHHmm(planned.normed.start_time); - const end_time = toStringFromHHmm(planned.normed.end_time); - const data: Partial = { bank_code_id: planned.normed.bank_code_id, - date: date, - start_time: start_time, - end_time: end_time, + date: planned.normed.date, + start_time: planned.normed.start_time, + end_time: planned.normed.end_time, is_remote: planned.update.is_remote, is_approved: planned.exist_shift.is_approved, comment: planned.update.comment, diff --git a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts index ef7fdd6..6810bfa 100644 --- a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -118,7 +118,7 @@ export class GetTimesheetsOverviewService { type: shift.bank_code?.type ?? '', is_remote: shift.is_remote ?? false, is_approved: shift.is_approved ?? false, - shift_id: shift.id ?? null, + id: shift.id ?? null, comment: shift.comment ?? null, })); diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts index 140ee97..4088444 100644 --- a/src/time-and-attendance/utils/type.utils.ts +++ b/src/time-and-attendance/utils/type.utils.ts @@ -2,7 +2,6 @@ import { Prisma, PrismaClient } from "@prisma/client"; import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto"; import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; -import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto"; import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; From 809209533a16d590130652d63fdc88d5693a3336 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Thu, 6 Nov 2025 14:00:19 -0500 Subject: [PATCH 51/57] fix(employee): setting up the profil section --- .../controllers/employees.controller.ts | 43 ++++------ .../employees/services/employees.service.ts | 78 ++++++++++--------- 2 files changed, 55 insertions(+), 66 deletions(-) diff --git a/src/identity-and-account/employees/controllers/employees.controller.ts b/src/identity-and-account/employees/controllers/employees.controller.ts index 8b58d4a..a3dd411 100644 --- a/src/identity-and-account/employees/controllers/employees.controller.ts +++ b/src/identity-and-account/employees/controllers/employees.controller.ts @@ -1,6 +1,8 @@ -import { Controller, Get, Patch, Param, Body, NotFoundException } from "@nestjs/common"; +import { Controller, Get, Patch, Param, Body, NotFoundException, Req, Post } from "@nestjs/common"; +import { Employees } from "@prisma/client"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes"; +import { CreateEmployeeDto } from "src/identity-and-account/employees/dtos/create-employee.dto"; import { EmployeeListItemDto } from "src/identity-and-account/employees/dtos/list-employee.dto"; import { EmployeeProfileItemDto } from "src/identity-and-account/employees/dtos/profil-employee.dto"; import { UpdateEmployeeDto } from "src/identity-and-account/employees/dtos/update-employee.dto"; @@ -15,8 +17,9 @@ export class EmployeesController { private readonly archiveService: EmployeesArchivalService, ) { } - @Get('profile/:email') - findOneProfile(@Param('email') email: string): Promise { + @Get('profile') + findOneProfile(@Req() req): Promise { + const email = req.user?.email; return this.employeesService.findOneProfile(email); } @@ -26,12 +29,13 @@ export class EmployeesController { return this.employeesService.findListEmployees(); } - @Patch(':email') + @Patch() @RolesAllowed(...MANAGER_ROLES) - async updateOrArchiveOrRestore(@Param('email') email: string, @Body() dto: UpdateEmployeeDto,) { + async updateOrArchiveOrRestore(@Req() req, @Body() dto: UpdateEmployeeDto,) { // if last_work_day is set => archive the employee // else if employee is archived and first_work_day or last_work_day = null => restore //otherwise => standard update + const email = req.user?.email; const result = await this.archiveService.patchEmployee(email, dto); if (!result) { throw new NotFoundException(`Employee with email: ${email} is not found in active or archive.`) @@ -39,29 +43,10 @@ export class EmployeesController { return result; } - - - //_____________________________________________________________________________________________ - // Deprecated or unused methods - //_____________________________________________________________________________________________ - - // @Post() - // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) - // @ApiOperation({summary: 'Create employee' }) - // @ApiResponse({ status: 201, description: 'Employee created', type: CreateEmployeeDto }) - // @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' }) - // create(@Body() dto: CreateEmployeeDto): Promise { - // return this.employeesService.create(dto); - // } - - // @Delete(':email') - // //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR ) - // @ApiOperation({summary: 'Delete employee' }) - // @ApiParam({ name: 'email', type: Number, description: 'Email of the employee to delete' }) - // @ApiResponse({ status: 204, description: 'Employee deleted' }) - // @ApiResponse({ status: 404, description: 'Employee not found' }) - // remove(@Param('email', ParseIntPipe) email: string): Promise { - // return this.employeesService.remove(email); - // } + @Post() + @RolesAllowed(...MANAGER_ROLES) + create(@Body() dto: CreateEmployeeDto): Promise { + return this.employeesService.create(dto); + } } diff --git a/src/identity-and-account/employees/services/employees.service.ts b/src/identity-and-account/employees/services/employees.service.ts index 50873b6..22cee41 100644 --- a/src/identity-and-account/employees/services/employees.service.ts +++ b/src/identity-and-account/employees/services/employees.service.ts @@ -1,4 +1,6 @@ import { Injectable, NotFoundException } from "@nestjs/common"; +import { Employees, Users } from "@prisma/client"; +import { CreateEmployeeDto } from "src/identity-and-account/employees/dtos/create-employee.dto"; import { EmployeeListItemDto } from "src/identity-and-account/employees/dtos/list-employee.dto"; import { EmployeeProfileItemDto } from "src/identity-and-account/employees/dtos/profil-employee.dto"; import { PrismaService } from "src/prisma/prisma.service"; @@ -88,48 +90,50 @@ export class EmployeesService { }; } + async create(dto: CreateEmployeeDto): Promise { + const { + first_name, + last_name, + email, + phone_number, + residence, + external_payroll_id, + company_code, + job_title, + first_work_day, + last_work_day, + is_supervisor, + } = dto; + + return this.prisma.$transaction(async (transaction) => { + const user: Users = await transaction.users.create({ + data: { + first_name, + last_name, + email, + phone_number, + residence, + }, + }); + return transaction.employees.create({ + data: { + user_id: user.id, + external_payroll_id, + company_code, + job_title, + first_work_day, + last_work_day, + is_supervisor, + }, + }); + }); + } + //_____________________________________________________________________________________________ // Deprecated or unused methods //_____________________________________________________________________________________________ - // async create(dto: CreateEmployeeDto): Promise { - // const { - // first_name, - // last_name, - // email, - // phone_number, - // residence, - // external_payroll_id, - // company_code, - // job_title, - // first_work_day, - // last_work_day, - // is_supervisor, - // } = dto; - // return this.prisma.$transaction(async (transaction) => { - // const user: Users = await transaction.users.create({ - // data: { - // first_name, - // last_name, - // email, - // phone_number, - // residence, - // }, - // }); - // return transaction.employees.create({ - // data: { - // user_id: user.id, - // external_payroll_id, - // company_code, - // job_title, - // first_work_day, - // last_work_day, - // is_supervisor, - // }, - // }); - // }); - // } // findAll(): Promise { // return this.prisma.employees.findMany({ From 7817c3b758972c42932cbe77dc364629cf7a3ded Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Thu, 6 Nov 2025 14:42:39 -0500 Subject: [PATCH 52/57] fix(shifts): fix the type update --- .../time-tracker/shifts/services/shifts-upsert.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 952ebc4..5008fe9 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -304,7 +304,7 @@ export class ShiftsUpsertService { date: update.date, start_time: update.start_time, end_time: update.end_time, - bank_code_id: exist_shift.bank_code_id, + bank_code_id: update.bank_code_id, }; return { update, exist_shift, normed }; }); From efc06b8857fbc0890efa286da8655e5781eacb1f Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Thu, 6 Nov 2025 14:45:16 -0500 Subject: [PATCH 53/57] fix(shifts): removed unused overtime calculation and return from create or update shifts --- .../shifts/services/shifts-upsert.service.ts | 21 +++---------------- src/time-and-attendance/utils/type.utils.ts | 2 +- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 5008fe9..0968ea0 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -4,7 +4,6 @@ import { Injectable, BadRequestException, ConflictException, NotFoundException } import { shift_select, timesheet_select } from "src/time-and-attendance/utils/selects.utils"; import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; -import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; import { PrismaService } from "src/prisma/prisma.service"; import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto"; @@ -15,7 +14,6 @@ import { response } from "express"; export class ShiftsUpsertService { constructor( private readonly prisma: PrismaService, - private readonly overtime: OvertimeService, private readonly emailResolver: EmailToIdResolver, private readonly typeResolver: BankCodesResolver, ) { } @@ -27,7 +25,6 @@ export class ShiftsUpsertService { //loads all shifts from a selected day to check for overlaping shifts //checks for overlaping shifts //create new shifts - //calculate overtime async createShifts(email: string, dtos: ShiftDto[]): Promise { if (!Array.isArray(dtos) || dtos.length === 0) return []; @@ -217,7 +214,6 @@ export class ShiftsUpsertService { const { type: bank_type } = await this.typeResolver.findTypeByBankCodeId(row.bank_code_id); - const summary = await this.overtime.getWeekOvertimeSummary(timesheet_id, normed.date, tx); const shift: GetShiftDto = { shift_id: row.id, @@ -230,7 +226,7 @@ export class ShiftsUpsertService { is_approved: false, comment: row.comment ?? undefined, }; - results[index] = { ok: true, data: { shift, overtime: summary } }; + results[index] = { ok: true, data: { shift } }; } return results; @@ -247,7 +243,6 @@ export class ShiftsUpsertService { // check for overlaping possibility // buil a set of data to manipulate modified data only // update shifts in DB - // recalculate overtime after update // return an updated version to display async updateShifts(dtos: ShiftDto[]): Promise { if (!Array.isArray(dtos) || dtos.length === 0) throw new BadRequestException({ error_code: 'SHIFT_MISSING' }); @@ -414,10 +409,6 @@ export class ShiftsUpsertService { data, select: shift_select, }); - const summary_new = await this.overtime.getWeekOvertimeSummary(row.timesheet_id, planned.exist_shift.date, tx); - if (row.date.getTime() !== planned.exist_shift.date.getTime()) { - await this.overtime.getWeekOvertimeSummary(row.timesheet_id, row.date, tx); - } const type = await this.typeResolver.findTypeByBankCodeId(row.bank_code_id); @@ -433,7 +424,7 @@ export class ShiftsUpsertService { comment: row.comment ?? undefined, }; - results.push({ ok: true, id: planned.exist_shift.id, data: { shift: dto, overtime: summary_new } }); + results.push({ ok: true, id: planned.exist_shift.id, data: { shift: dto } }); } catch (error) { throw new BadRequestException('INVALID_SHIFT'); } @@ -446,7 +437,6 @@ export class ShiftsUpsertService { // DELETE //_________________________________________________________________ //finds shifts using shit_ids - //recalc overtime shifts after delete //blocs deletion if approved async deleteShift(shift_id: number) { return await this.prisma.$transaction(async (tx) => { @@ -457,12 +447,7 @@ export class ShiftsUpsertService { if (!shift) throw new ConflictException({ error_code: 'SHIFT_INVALID' }); await tx.shifts.delete({ where: { id: shift_id } }); - - const summary = await this.overtime.getWeekOvertimeSummary(shift.timesheet_id, shift.date, tx); - return { - success: true, - overtime: summary - }; + return { success: true }; }); } diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts index 4088444..4f8ccdc 100644 --- a/src/time-and-attendance/utils/type.utils.ts +++ b/src/time-and-attendance/utils/type.utils.ts @@ -29,7 +29,7 @@ export type Normalized = { date: Date; start_time: Date; end_time: Date; bank_co export type ShiftWithOvertimeDto = { shift: GetShiftDto; - overtime: WeekOvertimeSummary; + // overtime: WeekOvertimeSummary; }; export type CreateShiftResult = { ok: true; data: ShiftWithOvertimeDto } | { ok: false; error: any }; From eb166dbc465a00a492c7669b0c5db29b0a1bec07 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Thu, 6 Nov 2025 16:45:00 -0500 Subject: [PATCH 54/57] clean(shifts): clean module of unused features --- .../shifts/controllers/shift.controller.ts | 1 - .../shifts/dtos/shift-update.dto.ts | 11 ------- .../shifts/services/shifts-upsert.service.ts | 30 +++++++++---------- .../time-tracker/shifts/shifts.module.ts | 5 +--- .../utils/selects.utils.ts | 1 - src/time-and-attendance/utils/type.utils.ts | 19 ++---------- 6 files changed, 18 insertions(+), 49 deletions(-) delete mode 100644 src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto.ts diff --git a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts index 70c3034..06b159d 100644 --- a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts @@ -1,6 +1,5 @@ import { BadRequestException, Body, Controller, Delete, Param, Patch, Post, Req } from "@nestjs/common"; import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; -import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service"; import { CreateShiftResult, UpdateShiftResult } from "src/time-and-attendance/utils/type.utils"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; diff --git a/src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto.ts b/src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto.ts deleted file mode 100644 index aef90e3..0000000 --- a/src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { PartialType, OmitType } from "@nestjs/swagger"; -import { IsInt } from "class-validator"; -import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; - -export class UpdateShiftDto extends PartialType( - // allows update using ShiftDto and preventing OmitType variables to be modified - OmitType(ShiftDto, ['is_approved', 'timesheet_id'] as const), -) { - @IsInt() - shift_id!: number; -} diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 0968ea0..510c5c3 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -8,7 +8,6 @@ import { PrismaService } from "src/prisma/prisma.service"; import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto"; import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; -import { response } from "express"; @Injectable() export class ShiftsUpsertService { @@ -226,7 +225,7 @@ export class ShiftsUpsertService { is_approved: false, comment: row.comment ?? undefined, }; - results[index] = { ok: true, data: { shift } }; + results[index] = { ok: true, data: shift }; } return results; @@ -341,19 +340,18 @@ export class ShiftsUpsertService { { start: planned.normed.start_time, end: planned.normed.end_time, date: planned.normed.date }) ); if (conflict) { - return updates.map(exist => - exist.id === planned.exist_shift.id - ? ({ - ok: false, id: exist.id, error: { - error_code: 'SHIFT_OVERLAP', - conflicts: { - start_time: toStringFromHHmm(conflict.start), - end_time: toStringFromHHmm(conflict.end), - date: toStringFromDate(conflict.date), - }, - } - } as UpdateShiftResult) - : ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to overlap in another update') }) + return updates.map(exist => exist.id === planned.exist_shift.id + ? ({ + ok: false, id: exist.id, error: { + error_code: 'SHIFT_OVERLAP', + conflicts: { + start_time: toStringFromHHmm(conflict.start), + end_time: toStringFromHHmm(conflict.end), + date: toStringFromDate(conflict.date), + }, + } + } as UpdateShiftResult) + : ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to overlap in another update') }) ); } } @@ -424,7 +422,7 @@ export class ShiftsUpsertService { comment: row.comment ?? undefined, }; - results.push({ ok: true, id: planned.exist_shift.id, data: { shift: dto } }); + results.push({ ok: true, id: planned.exist_shift.id, data: dto }); } catch (error) { throw new BadRequestException('INVALID_SHIFT'); } diff --git a/src/time-and-attendance/time-tracker/shifts/shifts.module.ts b/src/time-and-attendance/time-tracker/shifts/shifts.module.ts index 3dc407d..6d3773e 100644 --- a/src/time-and-attendance/time-tracker/shifts/shifts.module.ts +++ b/src/time-and-attendance/time-tracker/shifts/shifts.module.ts @@ -1,14 +1,11 @@ -import { BusinessLogicsModule } from 'src/time-and-attendance/domains/business-logics.module'; import { Module } from '@nestjs/common'; import { ShiftController } from 'src/time-and-attendance/time-tracker/shifts/controllers/shift.controller'; -import { ShiftsGetService } from 'src/time-and-attendance/time-tracker/shifts/services/shifts-get.service'; import { ShiftsUpsertService } from 'src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service'; @Module({ - imports: [ BusinessLogicsModule ], controllers: [ShiftController], - providers: [ ShiftsGetService, ShiftsUpsertService ], + providers: [ ShiftsUpsertService ], exports: [ ShiftsUpsertService ], }) export class ShiftsModule {} diff --git a/src/time-and-attendance/utils/selects.utils.ts b/src/time-and-attendance/utils/selects.utils.ts index fd8476e..98a27ec 100644 --- a/src/time-and-attendance/utils/selects.utils.ts +++ b/src/time-and-attendance/utils/selects.utils.ts @@ -1,5 +1,4 @@ import { Prisma } from "@prisma/client"; -import { dmmfToRuntimeDataModel } from "@prisma/client/runtime/library"; export const expense_select = { id: true, diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts index 4f8ccdc..2d2ef35 100644 --- a/src/time-and-attendance/utils/type.utils.ts +++ b/src/time-and-attendance/utils/type.utils.ts @@ -4,7 +4,6 @@ import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense- import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto"; -import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; @@ -26,24 +25,15 @@ export type TotalExpenses = { }; export type Normalized = { date: Date; start_time: Date; end_time: Date; bank_code_id: number}; - -export type ShiftWithOvertimeDto = { - shift: GetShiftDto; - // overtime: WeekOvertimeSummary; -}; - -export type CreateShiftResult = { ok: true; data: ShiftWithOvertimeDto } | { ok: false; error: any }; -export type UpdateShiftChanges = Omit; -export type UpdateShiftPayload = { shift_id: number; dto: UpdateShiftChanges }; -export type UpdateShiftResult = { ok: true; id: number; data: ShiftWithOvertimeDto } | { ok: false; id: number; error: any }; -export type DeleteShiftResult = { ok: true; id: number; overtime: WeekOvertimeSummary } | { ok: false; id: number; error: any }; +export type CreateShiftResult = { ok: true; data: GetShiftDto } | { ok: false; error: any }; +export type UpdateShiftResult = { ok: true; id: number; data: GetShiftDto } | { ok: false; id: number; error: any }; export type DeletePresetResult = { ok: true; id: number; } | { ok: false; id: number; error: any }; export type CreatePresetResult = { ok: true; } | { ok: false; error: any }; export type UpdatePresetResult = { ok: true; id: number; data: SchedulePresetsDto } | { ok: false; id: number; error: any }; -export type NormalizedExpense = { date: Date; comment: string; supervisor_comment?: string; }; +export type NormalizedExpense = { date: Date; comment: string; supervisor_comment?: string; }; export type CreateExpenseResult = { ok: true; data: GetExpenseDto } | { ok: false; error: any }; export type UpdateExpensePayload = { id: number; dto: updateExpenseDto }; export type UpdateExpenseResult = { ok: true; id: number; data: GetExpenseDto } | { ok: false; id: number; error: any }; @@ -52,7 +42,6 @@ export type DeleteExpenseResult = { ok: true; id: number; } | { ok: false; id: n export type NormedOk = { index: number; dto: ShiftEntity; normed: Normalized, timesheet_id: number }; -export type NormedErr = { index: number; error: any }; export type ShiftResponse = { @@ -79,8 +68,6 @@ export type ApplyResult = { export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>; -export type UpsertAction = 'create' | 'update' | 'delete'; - export type Tx = Prisma.TransactionClient | PrismaClient; export type WeekOvertimeSummary = { From 8dca65d00eacecf74f126ac20b008a3b27bce20e Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 7 Nov 2025 13:09:32 -0500 Subject: [PATCH 55/57] refactor(expenses): ajusted the controller and service to match new session set-up and did some cleaning --- .../controllers/expense.controller.ts | 13 +-- .../expenses/dtos/expense-create.dto.ts | 1 + .../expenses/dtos/expense-entity.dto.ts | 13 +++ .../expenses/dtos/expense-update.dto.ts | 6 - .../services/expense-upsert.service.ts | 110 ++++++++---------- ...ift-payload.dto.ts => shift-entity.dto.ts} | 0 .../shifts/services/shifts-upsert.service.ts | 2 +- src/time-and-attendance/utils/type.utils.ts | 31 +++-- 8 files changed, 89 insertions(+), 87 deletions(-) create mode 100644 src/time-and-attendance/expenses/dtos/expense-entity.dto.ts delete mode 100644 src/time-and-attendance/expenses/dtos/expense-update.dto.ts rename src/time-and-attendance/time-tracker/shifts/dtos/{shift-payload.dto.ts => shift-entity.dto.ts} (100%) diff --git a/src/time-and-attendance/expenses/controllers/expense.controller.ts b/src/time-and-attendance/expenses/controllers/expense.controller.ts index fb81eb2..9d9b08d 100644 --- a/src/time-and-attendance/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/expenses/controllers/expense.controller.ts @@ -1,17 +1,16 @@ import { Controller, Post, Param, Body, Patch, Delete, Req, UnauthorizedException } from "@nestjs/common"; -import { CreateExpenseResult, UpdateExpenseResult } from "src/time-and-attendance/utils/type.utils"; +import { CreateExpenseResult } from "src/time-and-attendance/utils/type.utils"; import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service"; -import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto"; import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { Roles as RoleEnum } from '.prisma/client'; +import { GLOBAL_CONTROLLER_ROLES } from "src/common/shared/role-groupes"; @Controller('expense') +@RolesAllowed(...GLOBAL_CONTROLLER_ROLES) export class ExpenseController { constructor( private readonly upsert_service: ExpenseUpsertService ){} @Post('create') - @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) create( @Req() req, @Body() dto: ExpenseDto): Promise{ const email = req.user?.email; if(!email) throw new UnauthorizedException('Unauthorized User'); @@ -19,13 +18,11 @@ export class ExpenseController { } @Patch('update') - @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) - update(@Body() body: { update :{ id: number; dto: updateExpenseDto }}): Promise{ - return this.upsert_service.updateExpense(body.update); + update(@Body() dto: ExpenseDto): Promise{ + return this.upsert_service.updateExpense(dto); } @Delete('delete/:expense_id') - @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) remove(@Param('expense_id') expense_id: number) { return this.upsert_service.deleteExpense(expense_id); } diff --git a/src/time-and-attendance/expenses/dtos/expense-create.dto.ts b/src/time-and-attendance/expenses/dtos/expense-create.dto.ts index 7e037e8..9b028cc 100644 --- a/src/time-and-attendance/expenses/dtos/expense-create.dto.ts +++ b/src/time-and-attendance/expenses/dtos/expense-create.dto.ts @@ -1,6 +1,7 @@ import { IsBoolean, IsInt, IsOptional, IsString, MaxLength } from "class-validator"; export class ExpenseDto { + @IsInt() @IsOptional() id: number; @IsInt() bank_code_id!: number; @IsInt() timesheet_id!: number; @IsInt() @IsOptional() attachment?: number; diff --git a/src/time-and-attendance/expenses/dtos/expense-entity.dto.ts b/src/time-and-attendance/expenses/dtos/expense-entity.dto.ts new file mode 100644 index 0000000..f04c989 --- /dev/null +++ b/src/time-and-attendance/expenses/dtos/expense-entity.dto.ts @@ -0,0 +1,13 @@ + +export class ExpenseEntity { + id: number; + timesheet_id: number; + bank_code_id: number; + attachment?:number; + date: Date; + amount?: number; + mileage?:number; + comment: string; + supervisor_comment?:string; + is_approved: boolean; +} \ No newline at end of file diff --git a/src/time-and-attendance/expenses/dtos/expense-update.dto.ts b/src/time-and-attendance/expenses/dtos/expense-update.dto.ts deleted file mode 100644 index 3dfe9b6..0000000 --- a/src/time-and-attendance/expenses/dtos/expense-update.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { OmitType, PartialType } from "@nestjs/swagger"; -import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; - -export class updateExpenseDto extends PartialType ( - OmitType(ExpenseDto, ['is_approved', 'timesheet_id'] as const) -){} \ No newline at end of file diff --git a/src/time-and-attendance/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/expenses/services/expense-upsert.service.ts index 7f16064..89c2b44 100644 --- a/src/time-and-attendance/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/expenses/services/expense-upsert.service.ts @@ -1,4 +1,4 @@ -import { CreateExpenseResult, UpdateExpensePayload, UpdateExpenseResult, DeleteExpenseResult, NormalizedExpense } from "src/time-and-attendance/utils/type.utils"; +import { CreateExpenseResult, DeleteExpenseResult, NormalizedExpense } from "src/time-and-attendance/utils/type.utils"; import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils"; import { Injectable, NotFoundException } from "@nestjs/common"; import { expense_select } from "src/time-and-attendance/utils/selects.utils"; @@ -6,6 +6,7 @@ import { PrismaService } from "src/prisma/prisma.service"; import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto"; import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; +import { ExpenseEntity } from "src/time-and-attendance/expenses/dtos/expense-entity.dto"; @Injectable() @@ -18,39 +19,28 @@ export class ExpenseUpsertService { //_________________________________________________________________ // CREATE //_________________________________________________________________ - async createExpense( dto: ExpenseDto, email: string): Promise { + async createExpense(dto: ExpenseDto, email: string): Promise { try { //fetch employee_id using req.user.email const employee_id = await this.emailResolver.findIdByEmail(email); - //normalize strings and dates - const normed_expense = this.normalizeExpenseDto(dto); + //normalize strings and dates and Parse numbers + const normed_expense = this.normalizeAndParseExpenseDto(dto); - //finds the timesheet using expense.date + //finds the timesheet using expense.date by finding the sunday const start_date = weekStartSunday(normed_expense.date); - - //parse numbers - const parsed_amount = this.parseOptionalNumber(dto.amount, "amount"); - const parsed_mileage = this.parseOptionalNumber(dto.mileage, "mileage"); - const parsed_attachment = this.parseOptionalNumber(dto.attachment, "attachment"); - const timesheet = await this.prisma.timesheets.findFirst({ where: { start_date, employee_id }, select: { id: true, employee_id: true }, }); - if(!timesheet) throw new NotFoundException(`Timesheet with id ${dto.timesheet_id} not found`); - + if (!timesheet) throw new NotFoundException(`Timesheet with id ${dto.timesheet_id} not found`); + //create a new expense const expense = await this.prisma.expenses.create({ data: { + ...normed_expense, timesheet_id: timesheet.id, bank_code_id: dto.bank_code_id, - attachment: parsed_attachment, - date: normed_expense.date, - amount: parsed_amount, - mileage: parsed_mileage, - comment: normed_expense.comment, - supervisor_comment: normed_expense.supervisor_comment, is_approved: dto.is_approved, }, //return the newly created expense with id @@ -59,16 +49,12 @@ export class ExpenseUpsertService { //build an object to return to the frontend to display const created: GetExpenseDto = { - id: expense.id, - timesheet_id: expense.timesheet_id, - bank_code_id: expense.bank_code_id, - attachment: expense.attachment ?? undefined, + ...expense, date: toStringFromDate(expense.date), amount: expense.amount?.toNumber(), mileage: expense.mileage?.toNumber(), - comment: expense.comment, + attachment: expense.attachment ?? undefined, supervisor_comment: expense.supervisor_comment ?? undefined, - is_approved: expense.is_approved, }; return { ok: true, data: created } @@ -80,47 +66,39 @@ export class ExpenseUpsertService { //_________________________________________________________________ // UPDATE //_________________________________________________________________ - async updateExpense({id, dto}: UpdateExpensePayload): Promise { + async updateExpense(dto: ExpenseDto): Promise { try { - //checks for modifications - const data: Record = {}; - if (dto.date !== undefined) data.date = toDateFromString(dto.date); - if (dto.comment !== undefined) data.comment = this.truncate280(dto.comment); - if (dto.attachment !== undefined) data.attachment = this.parseOptionalNumber(dto.attachment, "attachment"); - if (dto.amount !== undefined) data.amount = this.parseOptionalNumber(dto.amount, "amount"); - if (dto.mileage !== undefined) data.mileage = this.parseOptionalNumber(dto.mileage, "mileage"); - if (dto.bank_code_id !== undefined) data.bank_code_id = dto.bank_code_id; - if (dto.supervisor_comment !== undefined) { - data.supervisor_comment = dto.supervisor_comment?.trim() - ? this.truncate280(dto.supervisor_comment.trim()) - : null; - } - //return an error if no fields needs an update - if(!Object.keys(data).length) { - return { ok: false, id, error: new Error("Nothing to update")}; - } + //normalize string , date format and parse numbers + const normed_expense = this.normalizeAndParseExpenseDto(dto); + //checks for modifications + const data: ExpenseEntity = { + ...normed_expense, + id: dto.id, + timesheet_id: dto.timesheet_id, + bank_code_id: dto.bank_code_id, + is_approved: dto.is_approved, + }; + + //push updates and get updated datas const expense = await this.prisma.expenses.update({ - where: { id }, + where: { id: dto.id, timesheet_id: dto.timesheet_id }, data, select: expense_select, }); + //build an object to return to the frontend const updated: GetExpenseDto = { - id: expense.id, - timesheet_id: expense.timesheet_id, - bank_code_id: expense.bank_code_id, - attachment: expense.attachment ?? undefined, + ...expense, date: toStringFromDate(expense.date), amount: expense.amount?.toNumber(), mileage: expense.mileage?.toNumber(), - comment: expense.comment, + attachment: expense.attachment ?? undefined, supervisor_comment: expense.supervisor_comment ?? undefined, - is_approved: expense.is_approved, }; - return { ok: true, id: expense.id, data: updated }; + return updated; } catch (error) { - return { ok: false, id: id, error: error} + return error; } } //_________________________________________________________________ @@ -133,9 +111,9 @@ export class ExpenseUpsertService { where: { id: expense_id }, select: { id: true }, }); - if(!expense) throw new NotFoundException(`Expense with id: ${expense_id} not found`); - - await tx.expenses.delete({ where: { id: expense_id }}); + if (!expense) throw new NotFoundException(`Expense with id: ${expense_id} not found`); + + await tx.expenses.delete({ where: { id: expense_id } }); return { success: true }; }); return { ok: true, id: expense_id }; @@ -148,14 +126,22 @@ export class ExpenseUpsertService { // LOCAL HELPERS //_________________________________________________________________ //makes sure that comments are the right length the date is of Date type - private normalizeExpenseDto(dto: ExpenseDto): NormalizedExpense { + private normalizeAndParseExpenseDto(dto: ExpenseDto): NormalizedExpense { + const parsed_attachment = this.parseOptionalNumber(dto.attachment, "attachment"); + const parsed_mileage = this.parseOptionalNumber(dto.mileage, "mileage"); + const parsed_amount = this.parseOptionalNumber(dto.amount, "amount"); + const comment = this.truncate280(dto.comment); + const supervisor_comment = dto.supervisor_comment && dto.supervisor_comment.trim() + ? this.truncate280(dto.supervisor_comment.trim()) : undefined; const date = toDateFromString(dto.date); - const comment = this.truncate280(dto.comment); - const supervisor_comment = - dto.supervisor_comment && dto.supervisor_comment.trim() - ? this.truncate280(dto.supervisor_comment.trim()) - : undefined; - return { date, comment, supervisor_comment }; + return { + date, + comment, + supervisor_comment, + parsed_amount, + parsed_attachment, + parsed_mileage + }; } //makes sure that a string cannot exceed 280 chars diff --git a/src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto.ts b/src/time-and-attendance/time-tracker/shifts/dtos/shift-entity.dto.ts similarity index 100% rename from src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto.ts rename to src/time-and-attendance/time-tracker/shifts/dtos/shift-entity.dto.ts diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 510c5c3..7a857b2 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -6,7 +6,7 @@ import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-ty import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; -import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto"; +import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-entity.dto"; import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; @Injectable() diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts index 2d2ef35..b38ecc8 100644 --- a/src/time-and-attendance/utils/type.utils.ts +++ b/src/time-and-attendance/utils/type.utils.ts @@ -1,9 +1,8 @@ import { Prisma, PrismaClient } from "@prisma/client"; import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto"; -import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; -import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto"; +import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-entity.dto"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; @@ -24,26 +23,38 @@ export type TotalExpenses = { mileage: number; }; -export type Normalized = { date: Date; start_time: Date; end_time: Date; bank_code_id: number}; +export type Normalized = { + date: Date; + start_time: Date; + end_time: Date; + bank_code_id: number; +}; export type CreateShiftResult = { ok: true; data: GetShiftDto } | { ok: false; error: any }; export type UpdateShiftResult = { ok: true; id: number; data: GetShiftDto } | { ok: false; id: number; error: any }; +export type NormedOk = { + index: number; + dto: ShiftEntity; + normed: Normalized; + timesheet_id: number; +}; export type DeletePresetResult = { ok: true; id: number; } | { ok: false; id: number; error: any }; export type CreatePresetResult = { ok: true; } | { ok: false; error: any }; export type UpdatePresetResult = { ok: true; id: number; data: SchedulePresetsDto } | { ok: false; id: number; error: any }; -export type NormalizedExpense = { date: Date; comment: string; supervisor_comment?: string; }; +export type NormalizedExpense = { + date: Date; + comment: string; + supervisor_comment?: string; + parsed_amount?: number; + parsed_mileage?: number; + parsed_attachment?: number; +}; export type CreateExpenseResult = { ok: true; data: GetExpenseDto } | { ok: false; error: any }; -export type UpdateExpensePayload = { id: number; dto: updateExpenseDto }; -export type UpdateExpenseResult = { ok: true; id: number; data: GetExpenseDto } | { ok: false; id: number; error: any }; export type DeleteExpenseResult = { ok: true; id: number; } | { ok: false; id: number; error: any }; - -export type NormedOk = { index: number; dto: ShiftEntity; normed: Normalized, timesheet_id: number }; - - export type ShiftResponse = { week_day: string; sort_order: number; From 03d9fa2cf432273bd87015b8826b74ea8e445279 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 7 Nov 2025 16:49:16 -0500 Subject: [PATCH 56/57] clean(schedule-presets): clean module of unused imports --- docs/swagger/swagger-spec.json | 29 +++++++------------ .../controller/schedule-presets.controller.ts | 23 +++++++++------ 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 0f24da6..a3d57b2 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -500,24 +500,7 @@ "/schedule-presets/apply-presets": { "post": { "operationId": "SchedulePresetsController_applyPresets", - "parameters": [ - { - "name": "preset", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "start", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - } - ], + "parameters": [], "responses": { "201": { "description": "" @@ -556,6 +539,16 @@ "patch": { "operationId": "ExpenseController_update", "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExpenseDto" + } + } + } + }, "responses": { "200": { "description": "" diff --git a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index d96cf17..5e0c354 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -1,11 +1,10 @@ -import { Controller, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch, Req } from "@nestjs/common"; +import { Controller, Param, Query, Body, Get, Post, ParseIntPipe, Delete, Patch, Req } from "@nestjs/common"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; import { SchedulePresetsUpdateDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto"; import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; -import { Roles as RoleEnum } from '.prisma/client'; import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes"; @Controller('schedule-presets') @@ -28,14 +27,18 @@ export class SchedulePresetsController { //used to update an already existing schedule preset @Patch('update/:preset_id') @RolesAllowed(...MANAGER_ROLES) - async updatePreset(@Param('preset_id', ParseIntPipe) preset_id: number, @Body() dto: SchedulePresetsUpdateDto) { + async updatePreset( + @Param('preset_id', ParseIntPipe) preset_id: number, + @Body() dto: SchedulePresetsUpdateDto + ) { return await this.upsertService.updatePreset(preset_id, dto); } //used to delete a schedule preset @Delete('delete/:preset_id') - @RolesAllowed(RoleEnum.ADMIN) - async deletePreset(@Param('preset_id') preset_id: number) { + @RolesAllowed(...MANAGER_ROLES) + async deletePreset( + @Param('preset_id', ParseIntPipe) preset_id: number) { return await this.upsertService.deletePreset(preset_id); } @@ -50,10 +53,12 @@ export class SchedulePresetsController { //used to apply a preset to a timesheet @Post('apply-presets') - async applyPresets(@Req() req, @Query('preset') preset_name: string, @Query('start') start_date: string) { + async applyPresets( + @Req() req, + @Body('preset') preset_name: string, + @Body('start') start_date: string + ) { const email = req.user?.email; - if (!preset_name?.trim()) throw new BadRequestException('Query "preset" is required'); - if (!start_date?.trim()) throw new BadRequestException('Query "start" is required YYYY-MM-DD'); - return this.applyPresetsService.applyToTimesheet(email, preset_name, start_date); + return this.applyPresetsService.applyToTimesheet(email, preset_name, start_date); } } \ No newline at end of file From 6332a42fa746d8e0d53ef16cfea00b5cce34dc7b Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 10 Nov 2025 11:16:10 -0500 Subject: [PATCH 57/57] feat(presets): small ajustements --- .../controller/schedule-presets.controller.ts | 4 +- .../dtos/create-schedule-presets.dto.ts | 6 +- .../schedule-presets-apply.service.ts | 7 +- .../schedule-presets-upsert.service.ts | 134 +++++++++--------- 4 files changed, 80 insertions(+), 71 deletions(-) diff --git a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index 5e0c354..a4a7b9e 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -55,10 +55,10 @@ export class SchedulePresetsController { @Post('apply-presets') async applyPresets( @Req() req, - @Body('preset') preset_name: string, + @Body('preset') preset_id: number, @Body('start') start_date: string ) { const email = req.user?.email; - return this.applyPresetsService.applyToTimesheet(email, preset_name, start_date); + return this.applyPresetsService.applyToTimesheet(email, preset_id, start_date); } } \ No newline at end of file diff --git a/src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts b/src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts index fc6b7d8..fb58f69 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts @@ -1,7 +1,11 @@ -import { ArrayMinSize, IsArray, IsBoolean, IsOptional, IsString } from "class-validator"; +import { ArrayMinSize, IsArray, IsBoolean, IsInt, IsOptional, IsString } from "class-validator"; import { SchedulePresetShiftsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto"; export class SchedulePresetsDto { + + @IsInt() + id!: number; + @IsString() name!: string; diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts index c055373..122c30c 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts @@ -11,18 +11,19 @@ import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-i export class SchedulePresetsApplyService { constructor( private readonly prisma: PrismaService, private readonly emailResolver: EmailToIdResolver) {} - async applyToTimesheet( email: string, preset_name: string, start_date_iso: string ): Promise { - if(!preset_name?.trim()) throw new BadRequestException('A preset_name is required'); + async applyToTimesheet( email: string, id: number, start_date_iso: string ): Promise { + if(!id) throw new BadRequestException(`Schedule preset with id: ${id} not found`); if(!DATE_ISO_FORMAT.test(start_date_iso)) throw new BadRequestException('start_date must be of format :YYYY-MM-DD'); const employee_id = await this.emailResolver.findIdByEmail(email); const preset = await this.prisma.schedulePresets.findFirst({ - where: { employee_id, name: preset_name }, + where: { employee_id, id }, include: { shifts: { orderBy: [{ week_day: 'asc'}, { sort_order: 'asc'}], select: { + id: true, week_day: true, sort_order: true, start_time: true, diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts index cb1938e..c590d57 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts @@ -10,127 +10,131 @@ import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-i @Injectable() export class SchedulePresetsUpsertService { constructor( - private readonly prisma: PrismaService, - private readonly typeResolver : BankCodesResolver, + private readonly prisma: PrismaService, + private readonly typeResolver: BankCodesResolver, private readonly emailResolver: EmailToIdResolver, - ){} + ) { } //_________________________________________________________________ // CREATE //_________________________________________________________________ - async createPreset( email: string, dto: SchedulePresetsDto): Promise { + async createPreset(email: string, dto: SchedulePresetsDto): Promise { try { const shifts_data = await this.resolveAndBuildPresetShifts(dto); const employee_id = await this.emailResolver.findIdByEmail(email); - if(!shifts_data) throw new BadRequestException(`Employee with email: ${email} or dto not found`); + if (!shifts_data) throw new BadRequestException(`Employee with email: ${email} or dto not found`); - await this.prisma.$transaction(async (tx)=> { - if(dto.is_default) { + await this.prisma.$transaction(async (tx) => { + if (dto.is_default) { await tx.schedulePresets.updateMany({ where: { is_default: true, employee_id }, - data: { is_default: false }, + data: { is_default: false }, }); } const created = await tx.schedulePresets.create({ data: { + id: dto.id, employee_id, - name: dto.name, + name: dto.name, is_default: !!dto.is_default, - shifts: { create: shifts_data}, + shifts: { create: shifts_data }, }, }); return created; }); return { ok: true }; - + } catch (error: unknown) { - return { ok: false, error }; + return { ok: false, error }; } } //_________________________________________________________________ // UPDATE //_________________________________________________________________ - async updatePreset( preset_id: number, dto: SchedulePresetsDto ): Promise { + async updatePreset(preset_id: number, dto: SchedulePresetsDto): Promise { try { const existing = await this.prisma.schedulePresets.findFirst({ - where: { id: preset_id }, - select: { - id:true, - is_default: true, + where: { id: preset_id }, + select: { + id: true, + is_default: true, employee_id: true, }, }); - if(!existing) throw new NotFoundException(`Preset "${dto.name}" not found`); + if (!existing) throw new NotFoundException(`Preset "${dto.name}" not found`); const shifts_data = await this.resolveAndBuildPresetShifts(dto); await this.prisma.$transaction(async (tx) => { - if(typeof dto.is_default === 'boolean'){ - if(dto.is_default) { + if (typeof dto.is_default === 'boolean') { + if (dto.is_default) { await tx.schedulePresets.updateMany({ - where: { + where: { employee_id: existing.employee_id, - is_default: true, - NOT: { id: existing.id }, + is_default: true, + NOT: { id: existing.id }, }, data: { is_default: false }, }); } await tx.schedulePresets.update({ where: { id: existing.id }, - data: { + data: { is_default: dto.is_default, name: dto.name, }, }); } - if(shifts_data.length <= 0) throw new BadRequestException('Preset shifts to update not found'); + if (shifts_data.length <= 0) throw new BadRequestException('Preset shifts to update not found'); await tx.schedulePresetShifts.deleteMany({ where: { preset_id: existing.id } }); - const create_many_data: Prisma.SchedulePresetShiftsCreateManyInput[] = - shifts_data.map((shift)=> { - if(!shift.bank_code || !('connect' in shift.bank_code) || typeof shift.bank_code.connect?.id !=='number'){ + const create_many_data: Prisma.SchedulePresetShiftsCreateManyInput[] = + shifts_data.map((shift) => { + if (!shift.bank_code || !('connect' in shift.bank_code) || typeof shift.bank_code.connect?.id !== 'number') { throw new NotFoundException(`Bank code is required for updates( ${shift.week_day}, ${shift.sort_order})`); } const bank_code_id = shift.bank_code.connect.id; return { - preset_id: existing.id, - week_day: shift.week_day, - sort_order: shift.sort_order, - start_time: shift.start_time, - end_time: shift.end_time, - is_remote: shift.is_remote ?? false, + preset_id: existing.id, + week_day: shift.week_day, + sort_order: shift.sort_order, + start_time: shift.start_time, + end_time: shift.end_time, + is_remote: shift.is_remote ?? false, bank_code_id: bank_code_id, }; - }); - await tx.schedulePresetShifts.createMany({data: create_many_data}); + }); + await tx.schedulePresetShifts.createMany({ data: create_many_data }); }); const saved = await this.prisma.schedulePresets.findUnique({ where: { id: existing.id }, - include: { shifts: { - orderBy: [{ week_day: 'asc' }, { sort_order: 'asc' }], - include: { bank_code: { select: { type: true }}}, - }}, + include: { + shifts: { + orderBy: [{ week_day: 'asc' }, { sort_order: 'asc' }], + include: { bank_code: { select: { type: true } } }, + } + }, }); - if(!saved) throw new NotFoundException(`Preset with id: ${existing.id} not found`); + if (!saved) throw new NotFoundException(`Preset with id: ${existing.id} not found`); const response_dto: SchedulePresetsDto = { + id: saved.id, name: saved.name, is_default: saved.is_default, - preset_shifts: saved.shifts.map((shift) => ({ - preset_id: shift.preset_id, - week_day: shift.week_day, + preset_shifts: saved.shifts.map((shift) => ({ + preset_id: shift.preset_id, + week_day: shift.week_day, sort_order: shift.sort_order, - type: shift.bank_code.type, + type: shift.bank_code.type, start_time: toHHmmFromDate(shift.start_time), - end_time: toHHmmFromDate(shift.end_time), - is_remote: shift.is_remote, + end_time: toHHmmFromDate(shift.end_time), + is_remote: shift.is_remote, })), }; return { ok: true, id: existing.id, data: response_dto }; - } catch (error){ + } catch (error) { return { ok: false, id: preset_id, error } } } @@ -138,22 +142,22 @@ export class SchedulePresetsUpsertService { //_________________________________________________________________ // DELETE //_________________________________________________________________ - async deletePreset( preset_id: number ): Promise { + async deletePreset(preset_id: number): Promise { try { await this.prisma.$transaction(async (tx) => { const preset = await tx.schedulePresets.findFirst({ where: { id: preset_id }, select: { id: true }, }); - if(!preset) throw new NotFoundException(`Preset with id ${ preset_id } not found`); - await tx.schedulePresets.delete({where: { id: preset_id } }); + if (!preset) throw new NotFoundException(`Preset with id ${preset_id} not found`); + await tx.schedulePresets.delete({ where: { id: preset_id } }); return { success: true }; }); return { ok: true, id: preset_id }; } catch (error) { - if(error) throw new NotFoundException(`Preset schedule with id ${ preset_id } not found`); + if (error) throw new NotFoundException(`Preset schedule with id ${preset_id} not found`); return { ok: false, id: preset_id, error }; } } @@ -161,19 +165,19 @@ export class SchedulePresetsUpsertService { //PRIVATE HELPERS //resolve bank_code_id using type and convert hours to TIME and valid shifts end/start - private async resolveAndBuildPresetShifts( + private async resolveAndBuildPresetShifts( dto: SchedulePresetsDto - ): Promise{ + ): Promise { - if(!dto.preset_shifts?.length) throw new NotFoundException(`Empty or preset shifts not found`); + if (!dto.preset_shifts?.length) throw new NotFoundException(`Empty or preset shifts not found`); - const types = Array.from(new Set(dto.preset_shifts.map((shift)=> shift.type))); + const types = Array.from(new Set(dto.preset_shifts.map((shift) => shift.type))); const bank_code_set = new Map(); for (const type of types) { const { id } = await this.typeResolver.findIdAndModifierByType(type); bank_code_set.set(type, id) - } + } const toTime = (hhmm: string) => new Date(`1970-01-01T${hhmm}:00.000Z`); const pair_set = new Set(); @@ -185,25 +189,25 @@ export class SchedulePresetsUpsertService { pair_set.add(key); } - const items: Prisma.SchedulePresetShiftsCreateWithoutPresetInput[] = dto.preset_shifts.map((shift)=> { + const items: Prisma.SchedulePresetShiftsCreateWithoutPresetInput[] = dto.preset_shifts.map((shift) => { const bank_code_id = bank_code_set.get(shift.type); - if(!bank_code_id) throw new NotFoundException(`Bank code not found for type ${shift.type}`); + if (!bank_code_id) throw new NotFoundException(`Bank code not found for type ${shift.type}`); if (!shift.start_time || !shift.end_time) { throw new BadRequestException(`start_time and end_time are required for (${shift.week_day}, ${shift.sort_order})`); } const start = toTime(shift.start_time); - const end = toTime(shift.end_time); - if(end.getTime() <= start.getTime()) { + const end = toTime(shift.end_time); + if (end.getTime() <= start.getTime()) { throw new ConflictException(`end_time must be > start_time ( day: ${shift.week_day}, order: ${shift.sort_order})`); } return { - week_day: shift.week_day as Weekday, + week_day: shift.week_day as Weekday, sort_order: shift.sort_order, - bank_code: { connect: { id: bank_code_id} }, + bank_code: { connect: { id: bank_code_id } }, start_time: start, - end_time: end, - is_remote: !!shift.is_remote, + end_time: end, + is_remote: !!shift.is_remote, }; }); return items;