From 1a88e02411981fbd5b660e66d944b051c50ec440 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Thu, 13 Nov 2025 15:23:17 -0500 Subject: [PATCH] clean(modules): clean module imports and clean utils files --- docs/swagger/swagger-spec.json | 728 ++++++++++++------ src/app.module.ts | 42 +- .../mappers/bank-type-id.mapper.ts} | 0 .../mappers/email-id.mapper.ts} | 0 .../mappers/full-name.mapper.ts} | 0 .../mappers/shifts-id.mapper.ts} | 0 .../mappers/timesheet.mapper.ts} | 4 +- src/common/shared/role-groupes.ts | 5 + .../utils/constants.utils.ts | 5 + src/common/utils/date-utils.ts | 164 +++- .../employees/employees.module.ts | 22 +- .../identity-and-account.module.ts | 35 + .../controllers/bank-codes.controller.ts | 76 +- .../bank-codes/dtos/bank-code-entity.ts | 7 - src/modules/bank-codes/dtos/bank-code.dto.ts | 24 + .../bank-codes/dtos/create-bank-code.dto.ts | 46 -- .../bank-codes/dtos/update-bank-code.dto.ts | 4 - .../bank-codes/services/bank-codes.service.ts | 83 +- .../controllers/csv-exports.controller.ts | 11 +- .../domains/business-logics.module.ts | 2 +- .../domains/services/holiday.service.ts | 4 +- .../domains/services/overtime.service.ts | 2 +- .../expenses/expenses.module.ts | 3 +- .../services/expense-upsert.service.ts | 5 +- .../utils/leave-request.util.ts | 2 +- .../pay-period/pay-periods.module.ts | 2 +- .../services/pay-periods-query.service.ts | 4 +- .../time-and-attendance.module.ts | 24 +- .../dtos/create-schedule-preset-shifts.dto.ts | 2 +- .../schedule-presets-apply.service.ts | 5 +- .../services/schedule-presets-get.service.ts | 2 +- .../schedule-presets-upsert.service.ts | 6 +- .../shifts/services/shifts-create.service.ts | 6 +- .../shifts/services/shifts-get.service.ts | 6 +- .../services/shifts-update-delete.service.ts | 6 +- .../timesheet-get-overview.service.ts | 7 +- .../timesheets/timesheets.module.ts | 2 +- .../utils/date-time.utils.ts | 124 --- .../utils/mappers.utils.ts | 3 - 39 files changed, 835 insertions(+), 638 deletions(-) rename src/{time-and-attendance/utils/resolve-bank-type-id.utils.ts => common/mappers/bank-type-id.mapper.ts} (100%) rename src/{time-and-attendance/utils/resolve-email-id.utils.ts => common/mappers/email-id.mapper.ts} (100%) rename src/{time-and-attendance/utils/resolve-full-name.utils.ts => common/mappers/full-name.mapper.ts} (100%) rename src/{time-and-attendance/utils/resolve-shifts-id.utils.ts => common/mappers/shifts-id.mapper.ts} (100%) rename src/{time-and-attendance/utils/resolve-timesheet.utils.ts => common/mappers/timesheet.mapper.ts} (89%) rename src/{time-and-attendance => common}/utils/constants.utils.ts (80%) create mode 100644 src/identity-and-account/identity-and-account.module.ts delete mode 100644 src/modules/bank-codes/dtos/bank-code-entity.ts create mode 100644 src/modules/bank-codes/dtos/bank-code.dto.ts delete mode 100644 src/modules/bank-codes/dtos/create-bank-code.dto.ts delete mode 100644 src/modules/bank-codes/dtos/update-bank-code.dto.ts delete mode 100644 src/time-and-attendance/utils/date-time.utils.ts delete mode 100644 src/time-and-attendance/utils/mappers.utils.ts diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 7e75002..2cb7982 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -15,48 +15,6 @@ ] } }, - "/auth/v1/login": { - "get": { - "operationId": "AuthController_login", - "parameters": [], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "Auth" - ] - } - }, - "/auth/callback": { - "get": { - "operationId": "AuthController_loginCallback", - "parameters": [], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "Auth" - ] - } - }, - "/auth/me": { - "get": { - "operationId": "AuthController_getProfile", - "parameters": [], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "Auth" - ] - } - }, "/notifications/summary": { "get": { "operationId": "NotificationsController_summary", @@ -85,169 +43,6 @@ ] } }, - "/pay-periods/current-and-all": { - "get": { - "operationId": "PayPeriodsController_getCurrentAndAll", - "parameters": [ - { - "name": "date", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "PayPeriods" - ] - } - }, - "/pay-periods/date/{date}": { - "get": { - "operationId": "PayPeriodsController_findByDate", - "parameters": [ - { - "name": "date", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "PayPeriods" - ] - } - }, - "/pay-periods/{year}/{periodNumber}": { - "get": { - "operationId": "PayPeriodsController_findOneByYear", - "parameters": [ - { - "name": "year", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - }, - { - "name": "periodNumber", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "PayPeriods" - ] - } - }, - "/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": [ - { - "name": "year", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - }, - { - "name": "periodNumber", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "PayPeriods" - ] - } - }, - "/pay-periods/overview/{year}/{periodNumber}": { - "get": { - "operationId": "PayPeriodsController_getOverviewByYear", - "parameters": [ - { - "name": "year", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - }, - { - "name": "periodNumber", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "PayPeriods" - ] - } - }, "/timesheets/{year}/{period_number}": { "get": { "operationId": "TimesheetController_getTimesheetByPayPeriod", @@ -293,39 +88,6 @@ ] } }, - "/preferences": { - "patch": { - "operationId": "PreferencesController_updatePreferences", - "parameters": [ - { - "name": "PreferencesDto", - "required": true, - "in": "body", - "schema": { - "$ref": "#/components/schemas/PreferencesDto" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "number" - } - } - } - }, - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "Preferences" - ] - } - }, "/shift/create": { "post": { "operationId": "ShiftController_createBatch", @@ -581,6 +343,332 @@ "Expense" ] } + }, + "/pay-periods/current-and-all": { + "get": { + "operationId": "PayPeriodsController_getCurrentAndAll", + "parameters": [ + { + "name": "date", + "required": true, + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "PayPeriods" + ] + } + }, + "/pay-periods/date/{date}": { + "get": { + "operationId": "PayPeriodsController_findByDate", + "parameters": [ + { + "name": "date", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "PayPeriods" + ] + } + }, + "/pay-periods/{year}/{periodNumber}": { + "get": { + "operationId": "PayPeriodsController_findOneByYear", + "parameters": [ + { + "name": "year", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + }, + { + "name": "periodNumber", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "PayPeriods" + ] + } + }, + "/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": [ + { + "name": "year", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + }, + { + "name": "periodNumber", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "PayPeriods" + ] + } + }, + "/pay-periods/overview/{year}/{periodNumber}": { + "get": { + "operationId": "PayPeriodsController_getOverviewByYear", + "parameters": [ + { + "name": "year", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + }, + { + "name": "periodNumber", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "PayPeriods" + ] + } + }, + "/exports/csv": { + "get": { + "operationId": "CsvExportController_exportCsv", + "parameters": [], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "CsvExport" + ] + } + }, + "/auth/v1/login": { + "get": { + "operationId": "AuthController_login", + "parameters": [], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Auth" + ] + } + }, + "/auth/callback": { + "get": { + "operationId": "AuthController_loginCallback", + "parameters": [], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Auth" + ] + } + }, + "/auth/me": { + "get": { + "operationId": "AuthController_getProfile", + "parameters": [], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Auth" + ] + } + }, + "/employees/profile": { + "get": { + "operationId": "EmployeesController_findOneProfile", + "parameters": [], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Employees" + ] + } + }, + "/employees/employee-list": { + "get": { + "operationId": "EmployeesController_findListEmployees", + "parameters": [], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Employees" + ] + } + }, + "/employees": { + "patch": { + "operationId": "EmployeesController_updateOrArchiveOrRestore", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateEmployeeDto" + } + } + } + }, + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Employees" + ] + }, + "post": { + "operationId": "EmployeesController_create", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateEmployeeDto" + } + } + } + }, + "responses": { + "201": { + "description": "" + } + }, + "tags": [ + "Employees" + ] + } + }, + "/preferences": { + "patch": { + "operationId": "PreferencesController_updatePreferences", + "parameters": [ + { + "name": "PreferencesDto", + "required": true, + "in": "body", + "schema": { + "$ref": "#/components/schemas/PreferencesDto" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "number" + } + } + } + }, + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Preferences" + ] + } } }, "info": { @@ -640,14 +728,6 @@ } }, "schemas": { - "BulkCrewApprovalDto": { - "type": "object", - "properties": {} - }, - "PreferencesDto": { - "type": "object", - "properties": {} - }, "SchedulePresetsDto": { "type": "object", "properties": {} @@ -659,6 +739,162 @@ "ExpenseDto": { "type": "object", "properties": {} + }, + "BulkCrewApprovalDto": { + "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" + ] + }, + "PreferencesDto": { + "type": "object", + "properties": {} } } } diff --git a/src/app.module.ts b/src/app.module.ts index dbe9dd9..717c52e 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,36 +1,26 @@ import { BadRequestException, Module, ValidationPipe } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; -import { AuthenticationModule } from './identity-and-account/authentication/auth.module'; -// import { CsvExportModule } from './modules/exports/csv-exports.module'; -import { HealthModule } from './health/health.module'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; import { HealthController } from './health/health.controller'; import { NotificationsModule } from './modules/notifications/notifications.module'; -import { PreferencesModule } from './identity-and-account/preferences/preferences.module'; -import { PrismaModule } from './prisma/prisma.module'; -import { ScheduleModule } from '@nestjs/schedule'; -import { UsersModule } from './identity-and-account/users-management/users.module'; -import { ConfigModule } from '@nestjs/config'; +import { PrismaModule } from './prisma/prisma.module'; +import { ScheduleModule } from '@nestjs/schedule'; +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 { HttpExceptionFilter } from './common/filters/http-exception.filter'; +import { ValidationError } from 'class-validator'; import { TimeAndAttendanceModule } from 'src/time-and-attendance/time-and-attendance.module'; -import { PayperiodsModule } from 'src/time-and-attendance/pay-period/pay-periods.module'; +import { IdentityAndAccountModule } from 'src/identity-and-account/identity-and-account.module'; @Module({ imports: [ - AuthenticationModule, - ConfigModule.forRoot({isGlobal: true}), - // CsvExportModule, - HealthModule, + ConfigModule.forRoot({ isGlobal: true }), + ScheduleModule.forRoot(), //cronjobs NotificationsModule, - PayperiodsModule, - PreferencesModule, PrismaModule, - ScheduleModule.forRoot(), //cronjobs TimeAndAttendanceModule, - UsersModule, - ], + IdentityAndAccountModule, + ], controllers: [AppController, HealthController], providers: [ AppService, @@ -44,9 +34,9 @@ import { PayperiodsModule } from 'src/time-and-attendance/pay-period/pay-periods whitelist: true, forbidNonWhitelisted: true, transform: true, - exceptionFactory: (errors: ValidationError[] = [])=> { - const messages = errors.flatMap((e)=> Object.values(e.constraints ?? {})); - return new BadRequestException({ + exceptionFactory: (errors: ValidationError[] = []) => { + const messages = errors.flatMap((e) => Object.values(e.constraints ?? {})); + return new BadRequestException({ statusCode: 400, error: 'Bad Request', message: messages.length ? messages : errors, @@ -56,4 +46,4 @@ import { PayperiodsModule } from 'src/time-and-attendance/pay-period/pay-periods }, ], }) -export class AppModule {} +export class AppModule { } diff --git a/src/time-and-attendance/utils/resolve-bank-type-id.utils.ts b/src/common/mappers/bank-type-id.mapper.ts similarity index 100% rename from src/time-and-attendance/utils/resolve-bank-type-id.utils.ts rename to src/common/mappers/bank-type-id.mapper.ts diff --git a/src/time-and-attendance/utils/resolve-email-id.utils.ts b/src/common/mappers/email-id.mapper.ts similarity index 100% rename from src/time-and-attendance/utils/resolve-email-id.utils.ts rename to src/common/mappers/email-id.mapper.ts diff --git a/src/time-and-attendance/utils/resolve-full-name.utils.ts b/src/common/mappers/full-name.mapper.ts similarity index 100% rename from src/time-and-attendance/utils/resolve-full-name.utils.ts rename to src/common/mappers/full-name.mapper.ts diff --git a/src/time-and-attendance/utils/resolve-shifts-id.utils.ts b/src/common/mappers/shifts-id.mapper.ts similarity index 100% rename from src/time-and-attendance/utils/resolve-shifts-id.utils.ts rename to src/common/mappers/shifts-id.mapper.ts diff --git a/src/time-and-attendance/utils/resolve-timesheet.utils.ts b/src/common/mappers/timesheet.mapper.ts similarity index 89% rename from src/time-and-attendance/utils/resolve-timesheet.utils.ts rename to src/common/mappers/timesheet.mapper.ts index 4f70f94..85ef223 100644 --- a/src/time-and-attendance/utils/resolve-timesheet.utils.ts +++ b/src/common/mappers/timesheet.mapper.ts @@ -1,9 +1,9 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { Prisma, PrismaClient } from "@prisma/client"; -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"; +import { EmailToIdResolver } from "./email-id.mapper"; import { Result } from "src/common/errors/result-error.factory"; +import { weekStartSunday } from "src/common/utils/date-utils"; type Tx = Prisma.TransactionClient | PrismaClient; diff --git a/src/common/shared/role-groupes.ts b/src/common/shared/role-groupes.ts index b67c432..7f769c4 100644 --- a/src/common/shared/role-groupes.ts +++ b/src/common/shared/role-groupes.ts @@ -12,4 +12,9 @@ export const MANAGER_ROLES: readonly RoleEnum[] = [ RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN, +] + +export const PAY_SERVICE: readonly RoleEnum[] = [ + RoleEnum.HR, + RoleEnum.ACCOUNTING, ] \ No newline at end of file diff --git a/src/time-and-attendance/utils/constants.utils.ts b/src/common/utils/constants.utils.ts similarity index 80% rename from src/time-and-attendance/utils/constants.utils.ts rename to src/common/utils/constants.utils.ts index 2a53b28..09eaa0a 100644 --- a/src/time-and-attendance/utils/constants.utils.ts +++ b/src/common/utils/constants.utils.ts @@ -1,3 +1,5 @@ +import { Weekday } from "@prisma/client"; + export const NUMBER_OF_TIMESHEETS_TO_RETURN = 2; export const DAILY_LIMIT_HOURS = 8; export const WEEKLY_LIMIT_HOURS = 40; @@ -12,3 +14,6 @@ export const MS_PER_WEEK = 7 * 24 * 60 * 60 * 1000; //REGEX CONSTANTS export const DATE_ISO_FORMAT = /^\d{4}-\d{2}-\d{2}$/; export const HH_MM_REGEX = /^([01]\d|2[0-3]):[0-5]\d$/; + + +export const WEEKDAY: Weekday[] = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT']; \ No newline at end of file diff --git a/src/common/utils/date-utils.ts b/src/common/utils/date-utils.ts index 5d85548..33391c2 100644 --- a/src/common/utils/date-utils.ts +++ b/src/common/utils/date-utils.ts @@ -14,18 +14,6 @@ export function roundToQuarterHour(hours: number): number { return Math.round(hours *4) / 4; } -//calculate the number of the week (1 or 2) -export function computeWeekNumber(periodStart: Date, targetDate: Date): number { - const days = Math.floor( targetDate.getTime() - periodStart.getTime()) / - (1000 * 60 * 60 * 24); - return Math.floor(days / 7) +1; -} - -//Date format YYY-MM-DD -export function formatDateISO(d:Date): string { - return d.toISOString().split('T')[0]; -} - //fetch firts day of the week (Sunday) export function getWeekStart(date:Date, firstDayOfWeek = 0): Date { const d = new Date(date); @@ -49,29 +37,7 @@ export function getYearStart(date:Date): Date { return new Date(date.getFullYear(),0,1,0,0,0,0); } -export function getCurrentWeek(): { start_date_week: Date; end_date_week: Date } { - const now = new Date(); - const start_date_week = getWeekStart(now, 0); - const end_date_week = getWeekEnd(start_date_week); - return { start_date_week, end_date_week }; -} - -//cloning methods (helps with notify for overtime in a single day) -// export function toDateOnly(day: Date): Date { -// const d = new Date(day); -// d.setHours(0, 0, 0, 0); -// return d; -// } - -// export function composeDateWithTime(day: Date, timeOnly: Date): Date { -// const base = toDateOnly(day); -// base.setHours(timeOnly.getHours(), -// timeOnly.getMinutes(), -// timeOnly.getSeconds(), -// timeOnly.getMilliseconds()); -// return base; -// } - +//to be used to calculate breaks time export function hoursBetweenSameDay(day: Date, startTime: Date, endTime: Date): number { const start = new Date(day); start.setHours(startTime.getHours(), startTime.getMinutes(), @@ -85,7 +51,127 @@ export function hoursBetweenSameDay(day: Date, startTime: Date, endTime: Date): return ms / 3_600_000; // decimal hours } -// //utils to print Date into locale date format -// export function fmtDayLocal(day:Date, locale = 'fr-CA'): string { -// return new Date(day).toLocaleDateString(locale, { year: 'numeric', month: '2-digit', day: '2-digit'}); -// } +import { BadRequestException } from "@nestjs/common"; +import { ANCHOR_ISO, MS_PER_DAY, PERIODS_PER_YEAR, PERIOD_DAYS } from "src/common/utils/constants.utils"; + +//ensures the week starts from sunday +export function weekStartSunday(date_local: Date): Date { + const start_date = new Date(); + start_date.setDate(date_local.getDate() - date_local.getDay()); + return start_date; +} + +//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}`; +} + +//converts string to Date format +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); +} + +//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 to Date format from string +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())); +}; + +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 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)); +} + +export const overlaps = (a: { start: Date; end: Date, date?: Date; }, b: { start: Date; end: Date; date?: Date; }) => + ((a.date?.getTime() === b.date?.getTime()) && !(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/identity-and-account/employees/employees.module.ts b/src/identity-and-account/employees/employees.module.ts index 676f4b0..ceebf40 100644 --- a/src/identity-and-account/employees/employees.module.ts +++ b/src/identity-and-account/employees/employees.module.ts @@ -1,12 +1,12 @@ -// 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'; +import { Module } from '@nestjs/common'; +import { EmployeesController } from './controllers/employees.controller'; +import { EmployeesService } from './services/employees.service'; +import { EmployeesArchivalService } from 'src/identity-and-account/employees/services/employees-archival.service'; -// @Module({ -// imports: [SharedModule], -// controllers: [EmployeesController], -// providers: [EmployeesService], -// exports: [EmployeesService ], -// }) -// export class EmployeesModule {} +@Module({ + imports: [], + controllers: [EmployeesController], + providers: [EmployeesService, EmployeesArchivalService], + exports: [EmployeesService ], +}) +export class EmployeesModule {} diff --git a/src/identity-and-account/identity-and-account.module.ts b/src/identity-and-account/identity-and-account.module.ts new file mode 100644 index 0000000..979160d --- /dev/null +++ b/src/identity-and-account/identity-and-account.module.ts @@ -0,0 +1,35 @@ +import { Module } from "@nestjs/common"; +import { AuthModuleOptions } from "@nestjs/passport"; +import { AuthController } from "src/identity-and-account/authentication/controllers/auth.controller"; +import { AuthentikAuthService } from "src/identity-and-account/authentication/services/authentik-auth.service"; +import { EmployeesController } from "src/identity-and-account/employees/controllers/employees.controller"; +import { EmployeesModule } from "src/identity-and-account/employees/employees.module"; +import { EmployeesArchivalService } from "src/identity-and-account/employees/services/employees-archival.service"; +import { EmployeesService } from "src/identity-and-account/employees/services/employees.service"; +import { PreferencesController } from "src/identity-and-account/preferences/controllers/preferences.controller"; +import { PreferencesModule } from "src/identity-and-account/preferences/preferences.module"; +import { PreferencesService } from "src/identity-and-account/preferences/services/preferences.service"; +import { UsersService } from "src/identity-and-account/users-management/services/users.service"; +import { UsersModule } from "src/identity-and-account/users-management/users.module"; + +@Module({ + imports: [ + UsersModule, + EmployeesModule, + PreferencesModule, + AuthModuleOptions, + ], + controllers: [ + AuthController, + EmployeesController, + PreferencesController, + ], + providers: [ + AuthentikAuthService, + EmployeesArchivalService, + EmployeesService, + PreferencesService, + UsersService, + ], +}) +export class IdentityAndAccountModule { }; diff --git a/src/modules/bank-codes/controllers/bank-codes.controller.ts b/src/modules/bank-codes/controllers/bank-codes.controller.ts index 4588bbf..2e95855 100644 --- a/src/modules/bank-codes/controllers/bank-codes.controller.ts +++ b/src/modules/bank-codes/controllers/bank-codes.controller.ts @@ -1,49 +1,39 @@ -// 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 { BankCodeDto } from "../dtos/bank-code.dto"; +import { RolesAllowed } from "src/common/decorators/roles.decorators"; +import { PAY_SERVICE } from "src/common/shared/role-groupes"; -// @Controller('bank-codes') -// export class BankCodesControllers { -// constructor(private readonly bankCodesService: BankCodesService) {} -// //_____________________________________________________________________________________________ -// // Deprecated or unused methods -// //_____________________________________________________________________________________________ +@Controller('bank-codes') +@RolesAllowed(...PAY_SERVICE) +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() + create(@Body() dto: BankCodeDto) { + 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() + 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') + 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') + update(@Param('id', ParseIntPipe) id: number, @Body() dto: BankCodeDto) { + 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') + 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/bank-code-entity.ts b/src/modules/bank-codes/dtos/bank-code-entity.ts deleted file mode 100644 index 95e7756..0000000 --- a/src/modules/bank-codes/dtos/bank-code-entity.ts +++ /dev/null @@ -1,7 +0,0 @@ -export class BankCodeEntity { - id: number; - type: string; - categorie: string; - modifier: number; - bank_code: string; -} \ No newline at end of file diff --git a/src/modules/bank-codes/dtos/bank-code.dto.ts b/src/modules/bank-codes/dtos/bank-code.dto.ts new file mode 100644 index 0000000..0ad8409 --- /dev/null +++ b/src/modules/bank-codes/dtos/bank-code.dto.ts @@ -0,0 +1,24 @@ +import { Type } from "class-transformer"; +import { Allow, IsNotEmpty, IsNumber, IsString } from "class-validator"; + +export class BankCodeDto { + @Allow() + id: number; + + @IsString() + @IsNotEmpty() + type: string; + + @IsString() + @IsNotEmpty() + categorie: string; + + @Type(()=> Number) + @IsNumber() + @IsNotEmpty() + modifier: number; + + @IsString() + @IsNotEmpty() + bank_code: string; +} \ 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 deleted file mode 100644 index a08020a..0000000 --- a/src/modules/bank-codes/dtos/create-bank-code.dto.ts +++ /dev/null @@ -1,46 +0,0 @@ -// 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; - -// @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: '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 diff --git a/src/modules/bank-codes/dtos/update-bank-code.dto.ts b/src/modules/bank-codes/dtos/update-bank-code.dto.ts deleted file mode 100644 index 884e544..0000000 --- a/src/modules/bank-codes/dtos/update-bank-code.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -// import { PartialType } from "@nestjs/swagger"; -// import { CreateBankCodeDto } from "./create-bank-code.dto"; - -// 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 fbb8745..b45211d 100644 --- a/src/modules/bank-codes/services/bank-codes.service.ts +++ b/src/modules/bank-codes/services/bank-codes.service.ts @@ -1,51 +1,50 @@ -// 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 { BankCodeDto } from "../dtos/bank-code.dto"; +import { BankCodes } from "@prisma/client"; -// @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: BankCodeDto): 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: BankCodeDto) { + 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/exports/controllers/csv-exports.controller.ts b/src/modules/exports/controllers/csv-exports.controller.ts index 71cde76..628a569 100644 --- a/src/modules/exports/controllers/csv-exports.controller.ts +++ b/src/modules/exports/controllers/csv-exports.controller.ts @@ -1,21 +1,18 @@ -import { Controller, Get, Header, Query, UseGuards } from "@nestjs/common"; -import { RolesGuard } from "src/common/guards/roles.guard"; -import { Roles as RoleEnum } from '.prisma/client'; +import { Controller, Get, Header, Query} from "@nestjs/common"; import { CsvExportService } from "../services/csv-exports.service"; -// import { ExportCompany, ExportCsvOptionsDto, ExportType } from "../dtos/export-csv-options.dto"; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { ExportCsvOptionsDto } from "../dtos/export-csv-options.dto"; +import { RolesAllowed } from "src/common/decorators/roles.decorators"; +import { PAY_SERVICE } from "src/common/shared/role-groupes"; @Controller('exports') -@UseGuards(RolesGuard) +@RolesAllowed(...PAY_SERVICE) export class CsvExportController { constructor(private readonly csvService: CsvExportService) {} @Get('csv') @Header('Content-Type', 'text/csv; charset=utf-8') @Header('Content-Disposition', 'attachment; filename="export.csv"') - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.ACCOUNTING, RoleEnum.HR) async exportCsv(@Query() query: ExportCsvOptionsDto ): Promise { const rows = await this.csvService.collectTransaction( query.year, diff --git a/src/time-and-attendance/domains/business-logics.module.ts b/src/time-and-attendance/domains/business-logics.module.ts index 4dc801e..154e425 100644 --- a/src/time-and-attendance/domains/business-logics.module.ts +++ b/src/time-and-attendance/domains/business-logics.module.ts @@ -4,7 +4,7 @@ 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"; +import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; @Module({ diff --git a/src/time-and-attendance/domains/services/holiday.service.ts b/src/time-and-attendance/domains/services/holiday.service.ts index e19ae64..755df07 100644 --- a/src/time-and-attendance/domains/services/holiday.service.ts +++ b/src/time-and-attendance/domains/services/holiday.service.ts @@ -1,8 +1,8 @@ import { Injectable } from "@nestjs/common"; import { computeHours, getWeekStart } from "src/common/utils/date-utils"; 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"; +import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; +import { MS_PER_WEEK } from "src/common/utils/constants.utils"; import { Result } from "src/common/errors/result-error.factory"; /* le calcul est 1/20 des 4 dernières semaines, précédent la semaine incluant le férier. diff --git a/src/time-and-attendance/domains/services/overtime.service.ts b/src/time-and-attendance/domains/services/overtime.service.ts index acff2c4..9b7082c 100644 --- a/src/time-and-attendance/domains/services/overtime.service.ts +++ b/src/time-and-attendance/domains/services/overtime.service.ts @@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { Prisma, PrismaClient } from '@prisma/client'; import { getWeekStart, getWeekEnd, computeHours } from 'src/common/utils/date-utils'; import { PrismaService } from 'src/prisma/prisma.service'; -import { DAILY_LIMIT_HOURS, WEEKLY_LIMIT_HOURS } from 'src/time-and-attendance/utils/constants.utils'; +import { DAILY_LIMIT_HOURS, WEEKLY_LIMIT_HOURS } from 'src/common/utils/constants.utils'; type Tx = Prisma.TransactionClient | PrismaClient; diff --git a/src/time-and-attendance/expenses/expenses.module.ts b/src/time-and-attendance/expenses/expenses.module.ts index e6d737b..164d366 100644 --- a/src/time-and-attendance/expenses/expenses.module.ts +++ b/src/time-and-attendance/expenses/expenses.module.ts @@ -1,10 +1,11 @@ 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"; +import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; @Module({ controllers: [ ExpenseController ], - providers: [ ExpenseUpsertService ], + providers: [ ExpenseUpsertService, EmailToIdResolver ], }) export class ExpensesModule {} \ 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 a47900e..9b66466 100644 --- a/src/time-and-attendance/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/expenses/services/expense-upsert.service.ts @@ -1,5 +1,5 @@ -import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; + +import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; 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/expenses/dtos/expense-get.dto"; @@ -8,6 +8,7 @@ import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create import { Injectable } from "@nestjs/common"; import { Result } from "src/common/errors/result-error.factory"; import { NormalizedExpense } from "src/time-and-attendance/utils/type.utils"; +import { weekStartSunday, toStringFromDate, toDateFromString } from "src/common/utils/date-utils"; @Injectable() diff --git a/src/time-and-attendance/leave-requests/utils/leave-request.util.ts b/src/time-and-attendance/leave-requests/utils/leave-request.util.ts index 1fdceaa..5c10521 100644 --- a/src/time-and-attendance/leave-requests/utils/leave-request.util.ts +++ b/src/time-and-attendance/leave-requests/utils/leave-request.util.ts @@ -2,7 +2,7 @@ import { BadRequestException, Injectable } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { LeaveTypes } from "@prisma/client"; -import { toDateFromString, toStringFromDate } from "src/time-and-attendance/utils/date-time.utils"; +import { toDateFromString, toStringFromDate } from "src/common/utils/date-utils"; @Injectable() export class LeaveRequestsUtils { constructor( 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 aa49f23..9c0327d 100644 --- a/src/time-and-attendance/pay-period/pay-periods.module.ts +++ b/src/time-and-attendance/pay-period/pay-periods.module.ts @@ -3,7 +3,7 @@ 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 { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; @Module({ imports:[TimesheetsModule], diff --git a/src/time-and-attendance/pay-period/services/pay-periods-query.service.ts b/src/time-and-attendance/pay-period/services/pay-periods-query.service.ts index 86ce75e..b786a13 100644 --- a/src/time-and-attendance/pay-period/services/pay-periods-query.service.ts +++ b/src/time-and-attendance/pay-period/services/pay-periods-query.service.ts @@ -1,12 +1,10 @@ import { ForbiddenException, Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; -import { computeHours } from "src/common/utils/date-utils"; +import { computeHours, computePeriod, listPayYear, payYearOfDate } from "src/common/utils/date-utils"; import { PayPeriodOverviewDto } from "../dtos/overview-pay-period.dto"; import { EmployeePeriodOverviewDto } from "../dtos/overview-employee-period.dto"; 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 { constructor(private readonly prisma: PrismaService) { } diff --git a/src/time-and-attendance/time-and-attendance.module.ts b/src/time-and-attendance/time-and-attendance.module.ts index ad51972..0ff6657 100644 --- a/src/time-and-attendance/time-and-attendance.module.ts +++ b/src/time-and-attendance/time-and-attendance.module.ts @@ -16,21 +16,34 @@ import { TimesheetController } from "src/time-and-attendance/time-tracker/timesh 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"; -import { EmployeeTimesheetResolver } from "src/time-and-attendance/utils/resolve-timesheet.utils"; +import { BankCodesResolver } from "src/common/mappers/bank-type-id.mapper"; +import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; +import { EmployeeTimesheetResolver } from "src/common/mappers/timesheet.mapper"; +import { PayPeriodsController } from "src/time-and-attendance/pay-period/controllers/pay-periods.controller"; +import { ExpensesModule } from "src/time-and-attendance/expenses/expenses.module"; +import { PayPeriodsQueryService } from "src/time-and-attendance/pay-period/services/pay-periods-query.service"; +import { PayPeriodsCommandService } from "src/time-and-attendance/pay-period/services/pay-periods-command.service"; +import { CsvExportModule } from "src/modules/exports/csv-exports.module"; +import { CsvExportService } from "src/modules/exports/services/csv-exports.service"; +import { CsvExportController } from "src/modules/exports/controllers/csv-exports.controller"; @Module({ imports: [ BusinessLogicsModule, PayperiodsModule, - TimesheetsModule, + TimesheetsModule, + ExpensesModule, + PayperiodsModule, + CsvExportModule, ], controllers: [ TimesheetController, ShiftController, SchedulePresetsController, ExpenseController, + PayPeriodsController, + CsvExportController, + ], providers: [ GetTimesheetsOverviewService, @@ -45,6 +58,9 @@ import { EmployeeTimesheetResolver } from "src/time-and-attendance/utils/resolve BankCodesResolver, TimesheetApprovalService, EmployeeTimesheetResolver, + PayPeriodsQueryService, + PayPeriodsCommandService, + CsvExportService, ], exports: [TimesheetApprovalService ], }) export class TimeAndAttendanceModule { }; \ No newline at end of file diff --git a/src/time-and-attendance/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 index 5e37dcb..34787da 100644 --- a/src/time-and-attendance/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 @@ -1,5 +1,5 @@ import { IsBoolean, IsEnum, IsInt, IsOptional, IsString, Matches, Min } from "class-validator"; -import { HH_MM_REGEX } from "src/time-and-attendance/utils/constants.utils"; +import { HH_MM_REGEX } from "src/common/utils/constants.utils"; import { Weekday } from "@prisma/client"; export class SchedulePresetShiftsDto { 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 ba56edd..a564ae2 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 @@ -1,10 +1,9 @@ import { Injectable, BadRequestException, NotFoundException, ConflictException } from "@nestjs/common"; import { Weekday, Prisma } from "@prisma/client"; -import { DATE_ISO_FORMAT } from "src/time-and-attendance/utils/constants.utils"; +import { DATE_ISO_FORMAT, WEEKDAY } from "src/common/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/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; import { Result } from "src/common/errors/result-error.factory"; 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 b15765f..405d8c7 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 @@ -1,7 +1,7 @@ import { PresetResponse, ShiftResponse } from "src/time-and-attendance/utils/type.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { Injectable } from "@nestjs/common"; -import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; import { Result } from "src/common/errors/result-error.factory"; @Injectable() 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 c55f2db..01717e9 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 @@ -1,11 +1,11 @@ import { Injectable, BadRequestException, NotFoundException, ConflictException } from "@nestjs/common"; import { Prisma, Weekday } from "@prisma/client"; -import { toDateFromString, toHHmmFromDate } from "src/time-and-attendance/utils/date-time.utils"; import { PrismaService } from "src/prisma/prisma.service"; -import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; +import { BankCodesResolver } from "src/common/mappers/bank-type-id.mapper"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; -import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; import { Result } from "src/common/errors/result-error.factory"; +import { toHHmmFromDate, toDateFromString } from "src/common/utils/date-utils"; @Injectable() export class SchedulePresetsUpsertService { diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-create.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-create.service.ts index 76c7bdf..534df5c 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-create.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-create.service.ts @@ -1,12 +1,12 @@ import { 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 } from "@nestjs/common"; import { 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 { BankCodesResolver } from "src/common/mappers/bank-type-id.mapper"; +import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; import { PrismaService } from "src/prisma/prisma.service"; import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; import { Result } from "src/common/errors/result-error.factory"; +import { toStringFromHHmm, toStringFromDate, toDateFromString, overlaps, toHHmmFromString } from "src/common/utils/date-utils"; @Injectable() export class ShiftsCreateService { 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 96ae516..8f459de 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 @@ -1,8 +1,6 @@ -import { toStringFromDate, toStringFromHHmm } from "src/time-and-attendance/utils/date-time.utils"; -import { Injectable, NotFoundException } from "@nestjs/common"; + +import { Injectable } 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/time-tracker/shifts/dtos/shift-get.dto"; /** diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-update-delete.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-update-delete.service.ts index fed71ae..47344bf 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-update-delete.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-update-delete.service.ts @@ -1,12 +1,12 @@ -import { toDateFromString, toHHmmFromString, toStringFromHHmm, toStringFromDate, overlaps, toUTCDateFromString } from "src/time-and-attendance/utils/date-time.utils"; -import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; +import { BankCodesResolver } from "src/common/mappers/bank-type-id.mapper"; import { PrismaService } from "src/prisma/prisma.service"; import { shift_select } from "src/time-and-attendance/utils/selects.utils"; import { Injectable } from "@nestjs/common"; import { Normalized } from "src/time-and-attendance/utils/type.utils"; import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; import { Result } from "src/common/errors/result-error.factory"; -import { EmployeeTimesheetResolver } from "src/time-and-attendance/utils/resolve-timesheet.utils"; +import { EmployeeTimesheetResolver } from "src/common/mappers/timesheet.mapper"; +import { toDateFromString, toStringFromHHmm, toStringFromDate, toHHmmFromString, overlaps } from "src/common/utils/date-utils"; @Injectable() export class ShiftsUpdateDeleteService { 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 3f3291f..e6a067b 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 @@ -1,11 +1,12 @@ -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 { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/common/utils/constants.utils"; import { Injectable } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; -import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; import { Timesheet, Timesheets } from "src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto"; import { Result } from "src/common/errors/result-error.factory"; import { Prisma } from "@prisma/client"; +import { toDateFromString, sevenDaysFrom, toStringFromDate, toHHmmFromDate } from "src/common/utils/date-utils"; export type TotalHours = { regular: number; 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 9e96b4d..6590750 100644 --- a/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts +++ b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts @@ -3,7 +3,7 @@ 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 { 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'; +import { EmailToIdResolver } from 'src/common/mappers/email-id.mapper'; @Module({ controllers: [TimesheetController], diff --git a/src/time-and-attendance/utils/date-time.utils.ts b/src/time-and-attendance/utils/date-time.utils.ts deleted file mode 100644 index 6e88762..0000000 --- a/src/time-and-attendance/utils/date-time.utils.ts +++ /dev/null @@ -1,124 +0,0 @@ -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 -export function weekStartSunday(date_local: Date): Date { - const start_date = new Date(); - start_date.setDate(date_local.getDate() - date_local.getDay()); - return start_date; -} - -//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}`; -} - -//converts string to Date format -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); -} - -//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 to Date format from string -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())); -}; - -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 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)); -} - -export const overlaps = (a: { start: Date; end: Date, date?: Date; }, b: { start: Date; end: Date; date?: Date; }) => - ((a.date?.getTime() === b.date?.getTime()) && !(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/utils/mappers.utils.ts b/src/time-and-attendance/utils/mappers.utils.ts deleted file mode 100644 index 10a9faf..0000000 --- a/src/time-and-attendance/utils/mappers.utils.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Weekday } from "@prisma/client"; - -export const WEEKDAY: Weekday[] = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT']; \ No newline at end of file