From 48f1220a4e7b5bcbf027897850a60b501a52ed7f Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Tue, 18 Nov 2025 14:55:42 -0500 Subject: [PATCH] clean(modules): cleaned unused dto files and added validation. Changed error messages to match i18n --- docs/swagger/swagger-spec.json | 116 ------------------ src/common/mappers/bank-type-id.mapper.ts | 6 +- src/common/mappers/email-id.mapper.ts | 4 +- src/common/mappers/full-name.mapper.ts | 2 +- src/common/mappers/shifts-id.mapper.ts | 2 +- src/common/mappers/timesheet.mapper.ts | 2 +- .../expenses/dtos/expense-entity.dto.ts | 15 --- .../expenses/dtos/expense-get.dto.ts | 12 -- .../services/expense-upsert.service.ts | 26 ++-- .../services/pay-periods-command.service.ts | 2 + .../services/pay-periods-query.service.ts | 10 +- .../dtos/create-schedule-presets.dto.ts | 2 +- .../dtos/update-schedule-presets.dto.ts | 4 - .../schedule-presets.module.ts | 26 ++-- .../shifts/controllers/shift.controller.ts | 6 +- .../shifts/dtos/shift-get.dto.ts | 12 -- .../shifts/services/shifts-create.service.ts | 39 ++---- .../services/shifts-update-delete.service.ts | 36 ++---- .../shifts/shifts.module.ts | 6 +- .../time-and-attendance.module.ts | 19 ++- .../timesheets/dtos/timesheet.dto.ts | 93 +++++++------- .../timesheet-get-overview.service.ts | 10 +- 22 files changed, 130 insertions(+), 320 deletions(-) delete mode 100644 src/time-and-attendance/expenses/dtos/expense-entity.dto.ts delete mode 100644 src/time-and-attendance/expenses/dtos/expense-get.dto.ts delete mode 100644 src/time-and-attendance/schedule-presets/dtos/update-schedule-presets.dto.ts delete mode 100644 src/time-and-attendance/shifts/dtos/shift-get.dto.ts diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index e12e07a..87d2a87 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -207,114 +207,6 @@ ] } }, - "/schedule-presets/create": { - "post": { - "operationId": "SchedulePresetsController_createPreset", - "parameters": [], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SchedulePresetsDto" - } - } - } - }, - "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": "" - } - }, - "tags": [ - "SchedulePresets" - ] - } - }, - "/schedule-presets/delete/{preset_id}": { - "delete": { - "operationId": "SchedulePresetsController_deletePreset", - "parameters": [ - { - "name": "preset_id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "SchedulePresets" - ] - } - }, - "/schedule-presets/find-list": { - "get": { - "operationId": "SchedulePresetsController_findListById", - "parameters": [], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "SchedulePresets" - ] - } - }, - "/schedule-presets/apply-presets": { - "post": { - "operationId": "SchedulePresetsController_applyPresets", - "parameters": [], - "responses": { - "201": { - "description": "" - } - }, - "tags": [ - "SchedulePresets" - ] - } - }, "/expense/create": { "post": { "operationId": "ExpenseController_create", @@ -719,14 +611,6 @@ } }, "schemas": { - "SchedulePresetsDto": { - "type": "object", - "properties": {} - }, - "SchedulePresetsUpdateDto": { - "type": "object", - "properties": {} - }, "ExpenseDto": { "type": "object", "properties": {} diff --git a/src/common/mappers/bank-type-id.mapper.ts b/src/common/mappers/bank-type-id.mapper.ts index f887abf..be35579 100644 --- a/src/common/mappers/bank-type-id.mapper.ts +++ b/src/common/mappers/bank-type-id.mapper.ts @@ -17,7 +17,7 @@ export class BankCodesResolver { where: { type }, select: { id: true, modifier: true }, }); - if (!bank) return { success: false, error: `Unknown bank code type: ${type}` }; + if (!bank) return { success: false, error: `INVALID_TYPE` }; return { success: true, data: { id: bank.id, modifier: bank.modifier } }; }; @@ -30,7 +30,7 @@ export class BankCodesResolver { where: { type }, select: { id: true }, }); - if (!bank_code) return { success: false, error: `Unkown bank type: ${type}` }; + if (!bank_code) return { success: false, error: `INVALID_TYPE` }; return { success: true, data: bank_code.id }; } @@ -42,7 +42,7 @@ export class BankCodesResolver { where: { id: bank_code_id }, select: { type: true }, }); - if (!bank_code) return { success: false, error: `Type with id : ${bank_code_id} not found` } + if (!bank_code) return { success: false, error: `INVALID_TYPE` } return { success: true, data: bank_code.type }; } diff --git a/src/common/mappers/email-id.mapper.ts b/src/common/mappers/email-id.mapper.ts index 731c817..eccdcfb 100644 --- a/src/common/mappers/email-id.mapper.ts +++ b/src/common/mappers/email-id.mapper.ts @@ -18,7 +18,7 @@ export class EmailToIdResolver { where: { user: { email } }, select: { id: true }, }); - if (!employee) return { success: false, error: `Employee with email:${email} not found` }; + if (!employee) return { success: false, error: `EMPLOYEE_NOT_FOUND` }; return { data: employee.id, success: true }; } @@ -30,7 +30,7 @@ export class EmailToIdResolver { where: { email }, select: { id: true }, }); - if (!user) return { success: false, error: `User with email:${email} not found` }; + if (!user) return { success: false, error: `EMPLOYEE_NOT_FOUND` }; return { success: true, data: user.id }; } } \ No newline at end of file diff --git a/src/common/mappers/full-name.mapper.ts b/src/common/mappers/full-name.mapper.ts index ebb3df5..e193e03 100644 --- a/src/common/mappers/full-name.mapper.ts +++ b/src/common/mappers/full-name.mapper.ts @@ -15,7 +15,7 @@ export class FullNameResolver { where: { id: employee_id }, select: { user: { select: {first_name: true, last_name: true} } }, }); - if(!employee) return { success: false, error: `Unknown user with id ${employee_id}`} + if(!employee) return { success: false, error: `INVALID_EMPLOYEE`} const full_name = ( employee.user.first_name + " " + employee.user.last_name ) || " "; return {success: true, data: full_name }; diff --git a/src/common/mappers/shifts-id.mapper.ts b/src/common/mappers/shifts-id.mapper.ts index 35ae5e2..c50d703 100644 --- a/src/common/mappers/shifts-id.mapper.ts +++ b/src/common/mappers/shifts-id.mapper.ts @@ -32,7 +32,7 @@ export class ShiftIdResolver { }, select: { id: true }, }); - if (!shift) return { success: false, error: `shift not found` } + if (!shift) return { success: false, error: `SHIFT_NOT_FOUND` } return { success: true, data: shift.id }; }; diff --git a/src/common/mappers/timesheet.mapper.ts b/src/common/mappers/timesheet.mapper.ts index 85ef223..7474bf9 100644 --- a/src/common/mappers/timesheet.mapper.ts +++ b/src/common/mappers/timesheet.mapper.ts @@ -25,7 +25,7 @@ export class EmployeeTimesheetResolver { where: { employee_id : employee_id.data, start_date: start_date }, select: { id: true }, }); - if(!timesheet) throw new NotFoundException(`timesheet not found`); + if(!timesheet) throw new NotFoundException(`TIMESHEET_NOT_FOUND`); return { success: true, data: {id: timesheet.id} }; } } \ No newline at end of file diff --git a/src/time-and-attendance/expenses/dtos/expense-entity.dto.ts b/src/time-and-attendance/expenses/dtos/expense-entity.dto.ts deleted file mode 100644 index cf9a8d6..0000000 --- a/src/time-and-attendance/expenses/dtos/expense-entity.dto.ts +++ /dev/null @@ -1,15 +0,0 @@ -// import { BankCodeEntity } from "src/modules/bank-codes/dtos/bank-code-entity"; - -export class ExpenseEntity { - id: number; - timesheet_id: number; - bank_code_id: number; - attachment?:number | null; - date: Date; - amount?: number | null; - mileage?:number | null; - comment: string; - supervisor_comment?:string | null; - is_approved: boolean; - // bank_code?: BankCodeEntity; -} \ No newline at end of file diff --git a/src/time-and-attendance/expenses/dtos/expense-get.dto.ts b/src/time-and-attendance/expenses/dtos/expense-get.dto.ts deleted file mode 100644 index 773a1a7..0000000 --- a/src/time-and-attendance/expenses/dtos/expense-get.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -export class GetExpenseDto { - id: number; - timesheet_id: number; - bank_code_id: number; - attachment?: number; - date: string; - comment: string; - mileage?: number; - amount?: number; - supervisor_comment?: string; - is_approved: boolean; -} \ 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 f52fb0d..f29df61 100644 --- a/src/time-and-attendance/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/expenses/services/expense-upsert.service.ts @@ -38,7 +38,7 @@ export class ExpenseUpsertService { where: { start_date, employee_id: employee_id.data }, select: { id: true, employee_id: true }, }); - if (!timesheet) return { success: false, error: `Timesheet with id : ${dto.timesheet_id} not found` }; + if (!timesheet) return { success: false, error: `TIMESHEET_NOT_FOUND` }; //create a new expense const expense = await this.prisma.expenses.create({ @@ -50,7 +50,7 @@ export class ExpenseUpsertService { //return the newly created expense with id select: expense_select, }); - if (!expense) return { success: false, error: `An error occured during creation. Expense is invalid` }; + if (!expense) return { success: false, error: `INVALID_EXPENSE` }; //build an object to return to the frontend to display const created: ExpenseDto = { @@ -65,7 +65,7 @@ export class ExpenseUpsertService { return { success: true, data: created }; } catch (error) { - return { success: false, error: `An error occured during creation. Expense not created : ` + error }; + return { success: false, error: 'INVALID_EXPENSE' }; } } @@ -88,14 +88,14 @@ export class ExpenseUpsertService { where: { start_date: new_timesheet_start_date, employee_id: employee_id.data }, select: timesheet_select, }); - if (!timesheet) return { success: false, error: `Timesheet ${dto.timesheet_id} not found` } + if (!timesheet) return { success: false, error: `TIMESHEET_NOT_FOUND` } //checks for modifications const data: Prisma.ExpensesUpdateInput = { ...normed_expense.data, is_approved: dto.is_approved, }; - if (!data) return { success: false, error: `An error occured during normalization. Expense with id: ${dto.id} is invalid` } + if (!data) return { success: false, error: `INVALID_EXPENSE` } //push updates and get updated datas const expense = await this.prisma.expenses.update({ @@ -103,7 +103,7 @@ export class ExpenseUpsertService { data, select: expense_select, }); - if (!expense) return { success: false, error: `An error occured during update. Expense with id: ${dto.id} was not updated` } + if (!expense) return { success: false, error: 'INVALID_EXPENSE' } //build an object to return to the frontend const updated: ExpenseDto = { @@ -117,7 +117,7 @@ export class ExpenseUpsertService { }; return { success: true, data: updated }; } catch (error) { - return { success: false, error: (`Expense with id: ${dto.id} generated an error:` + error) }; + return { success: false, error: 'EXPENSE_NOT_FOUND' }; } } //_________________________________________________________________ @@ -130,20 +130,14 @@ export class ExpenseUpsertService { where: { id: expense_id }, select: { id: true }, }); - if (!expense) return { - success: false, - error: `An error occured during removal. Expense with id :${expense_id} was not found ` - }; + if (!expense) return { success: false, error: `EXPENSE_NOT_FOUND` }; await tx.expenses.delete({ where: { id: expense.id } }); return { success: true, data: expense.id }; }); return { success: true, data: expense_id }; } catch (error) { - return { - success: false, - error: `An error occured during removal. Expense with id :${expense_id} generated an error: ` + error - }; + return { success: false, error: `EXPENSE_NOT_FOUND` }; } } @@ -162,7 +156,7 @@ export class ExpenseUpsertService { const date = toDateFromString(dto.date); const type = await this.typeResolver.findBankCodeIDByType(dto.type); - if (!type.success) return { success: false, error: 'Bank-type not found' } + if (!type.success) return { success: false, error: 'INVALID_EXPENSE_TYPE' } return { success: true, 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 6022635..d014259 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 @@ -4,6 +4,8 @@ import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto"; import { PayPeriodsQueryService } from "./pay-periods-query.service"; import { TimesheetApprovalService } from "src/time-and-attendance/timesheets/services/timesheet-approval.service"; + + //change promise to return result pattern @Injectable() export class PayPeriodsCommandService { constructor( 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 b786a13..1e5e0c2 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 @@ -9,12 +9,14 @@ import { mapPayPeriodToDto } from "../mappers/pay-periods.mapper"; export class PayPeriodsQueryService { constructor(private readonly prisma: PrismaService) { } + //change promise to return result pattern + async getOverview(pay_period_no: number): Promise { const period = await this.prisma.payPeriods.findFirst({ where: { pay_period_no }, orderBy: { pay_year: "desc" }, }); - if (!period) throw new NotFoundException(`Period #${pay_period_no} not found`); + if (!period) throw new NotFoundException(`PAY_PERIOD_NOT_FOUND`); return this.buildOverview({ period_start: period.period_start, @@ -78,7 +80,7 @@ export class PayPeriodsQueryService { Promise { // 1) Search for the period const period = await this.prisma.payPeriods.findFirst({ where: { pay_year, pay_period_no: period_no } }); - if (!period) throw new NotFoundException(`Pay period ${pay_year}-${period_no} not found`); + if (!period) throw new NotFoundException(`PAY_PERIOD_NOT_FOUND`); // 2) fetch supervisor const supervisor = await this.prisma.employees.findFirst({ @@ -345,7 +347,7 @@ export class PayPeriodsQueryService { where: { pay_period_no: period_no }, orderBy: { pay_year: "desc" }, }); - if (!row) throw new NotFoundException(`Pay period #${period_no} not found`); + if (!row) throw new NotFoundException(`PAY_PERIOD_NOT_FOUND`); return mapPayPeriodToDto(row); } @@ -384,7 +386,7 @@ export class PayPeriodsQueryService { const pay_year = payYearOfDate(date); const periods = listPayYear(pay_year); const hit = periods.find(period => date >= period.period_start && date <= period.period_end); - if (!hit) throw new NotFoundException(`No period found for ${date}`); + if (!hit) throw new NotFoundException(`PAY_PERIOD_NOT_FOUND`); return { pay_period_no: hit.period_no, diff --git a/src/time-and-attendance/schedule-presets/dtos/create-schedule-presets.dto.ts b/src/time-and-attendance/schedule-presets/dtos/create-schedule-presets.dto.ts index fb58f69..7064ee4 100644 --- a/src/time-and-attendance/schedule-presets/dtos/create-schedule-presets.dto.ts +++ b/src/time-and-attendance/schedule-presets/dtos/create-schedule-presets.dto.ts @@ -1,5 +1,5 @@ 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"; +import { SchedulePresetShiftsDto } from "src/time-and-attendance/schedule-presets/dtos/create-schedule-preset-shifts.dto"; export class SchedulePresetsDto { diff --git a/src/time-and-attendance/schedule-presets/dtos/update-schedule-presets.dto.ts b/src/time-and-attendance/schedule-presets/dtos/update-schedule-presets.dto.ts deleted file mode 100644 index b8b9cb6..0000000 --- a/src/time-and-attendance/schedule-presets/dtos/update-schedule-presets.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -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/schedule-presets/schedule-presets.module.ts b/src/time-and-attendance/schedule-presets/schedule-presets.module.ts index b17863a..1c7801d 100644 --- a/src/time-and-attendance/schedule-presets/schedule-presets.module.ts +++ b/src/time-and-attendance/schedule-presets/schedule-presets.module.ts @@ -1,21 +1,21 @@ import { Module } from "@nestjs/common"; -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 { 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({ - controllers: [SchedulePresetsController], - providers: [ - SchedulePresetsUpsertService, - SchedulePresetsGetService, - SchedulePresetsApplyService, - ], + controllers: [/*SchedulePresetsController*/], + // providers: [ + // SchedulePresetsUpsertService, + // SchedulePresetsGetService, + // SchedulePresetsApplyService, + // ], exports:[ - SchedulePresetsUpsertService, - SchedulePresetsGetService, - SchedulePresetsApplyService, + // SchedulePresetsUpsertService, + // SchedulePresetsGetService, + // SchedulePresetsApplyService, ], }) export class SchedulePresetsModule {} \ No newline at end of file diff --git a/src/time-and-attendance/shifts/controllers/shift.controller.ts b/src/time-and-attendance/shifts/controllers/shift.controller.ts index 3c796c5..c0fa32d 100644 --- a/src/time-and-attendance/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/shifts/controllers/shift.controller.ts @@ -1,10 +1,10 @@ import { 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 { RolesAllowed } from "src/common/decorators/roles.decorators"; import { GLOBAL_CONTROLLER_ROLES } from "src/common/shared/role-groupes"; import { Result } from "src/common/errors/result-error.factory"; -import { ShiftsCreateService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-create.service"; -import { ShiftsUpdateDeleteService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-update-delete.service"; +import { ShiftDto } from "src/time-and-attendance/shifts/dtos/shift-create.dto"; +import { ShiftsCreateService } from "src/time-and-attendance/shifts/services/shifts-create.service"; +import { ShiftsUpdateDeleteService } from "src/time-and-attendance/shifts/services/shifts-update-delete.service"; @Controller('shift') diff --git a/src/time-and-attendance/shifts/dtos/shift-get.dto.ts b/src/time-and-attendance/shifts/dtos/shift-get.dto.ts deleted file mode 100644 index c5fd877..0000000 --- a/src/time-and-attendance/shifts/dtos/shift-get.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -export class GetShiftDto { - shift_id: number; - timesheet_id: number; - type: string; - date: string; - start_time: string; - end_time: string; - is_remote: boolean; - is_approved: boolean; - comment?: string; -} - diff --git a/src/time-and-attendance/shifts/services/shifts-create.service.ts b/src/time-and-attendance/shifts/services/shifts-create.service.ts index 534df5c..060a761 100644 --- a/src/time-and-attendance/shifts/services/shifts-create.service.ts +++ b/src/time-and-attendance/shifts/services/shifts-create.service.ts @@ -4,9 +4,9 @@ import { timesheet_select } from "src/time-and-attendance/utils/selects.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"; +import { ShiftDto } from "src/time-and-attendance/shifts/dtos/shift-create.dto"; @Injectable() export class ShiftsCreateService { @@ -63,26 +63,14 @@ export class ShiftsCreateService { try { //transform string format to date and HHmm const normed_shift = await this.normalizeShiftDto(dto); - if(!normed_shift.success) return { success: false, error: 'An error occured during normalization' } - if (normed_shift.data.end_time <= normed_shift.data.start_time) return { - success: false, - error: `INVALID_SHIFT - ` - + `start_time: ${toStringFromHHmm(normed_shift.data.start_time)},` - + `end_time: ${toStringFromHHmm(normed_shift.data.end_time)},` - + `date: ${toStringFromDate(normed_shift.data.date)}.` - } + if (!normed_shift.success) return { success: false, error: normed_shift.error }; + if (normed_shift.data.end_time <= normed_shift.data.start_time) return { success: false, error: `INVALID_SHIFT_TIME` }; //fetch the right timesheet const timesheet = await this.prisma.timesheets.findUnique({ where: { id: dto.timesheet_id, employee_id }, select: timesheet_select, }); - if (!timesheet) return { - success: false, - error: `INVALID_TIMESHEET -` - + `start_time: ${toStringFromHHmm(normed_shift.data.start_time)},` - + `end_time: ${toStringFromHHmm(normed_shift.data.end_time)},` - + `date: ${toStringFromDate(normed_shift.data.date)}.` - } + if (!timesheet) return { success: false, error: `INVALID_TIMESHEET` }; //finds bank_code_id using the type const bank_code_id = await this.typeResolver.findBankCodeIDByType(dto.type); if (!bank_code_id.success) return { success: false, error: bank_code_id.error }; @@ -102,13 +90,7 @@ export class ShiftsCreateService { { start: existing_start, end: existing_end, date: existing_date }, ); if (has_overlap) { - return { - success: false, - error: `SHIFT_OVERLAP` - + `new shift: ${toStringFromHHmm(normed_shift.data.start_time)}–${toStringFromHHmm(normed_shift.data.end_time)} ` - + `existing shift: ${toStringFromHHmm(existing.start_time)}–${toStringFromHHmm(existing.end_time)} ` - + `date: ${toStringFromDate(normed_shift.data.date)})`, - } + return { success: false, error: `SHIFT_OVERLAP` }; } } @@ -139,7 +121,7 @@ export class ShiftsCreateService { } return { success: true, data: shift }; } catch (error) { - return { success: false, error: `An error occured during creation, invalid data` }; + return { success: false, error: `INVALID_SHIFT` }; } } @@ -148,13 +130,14 @@ export class ShiftsCreateService { //_________________________________________________________________ //converts all string hours and date to Date and HHmm formats private normalizeShiftDto = async (dto: ShiftDto): Promise> => { - const bank_code_id = await this.typeResolver.findBankCodeIDByType(dto.type); - if(!bank_code_id.success) return { success: false, error: 'Bank_code not found'} + const bank_code_id = await this.typeResolver.findBankCodeIDByType(dto.type); + if (!bank_code_id.success) return { success: false, error: 'INVALID_SHIFT' } + //TODO: validate date and time to ensure "banana" is not accepted using an if statement and a REGEX const date = toDateFromString(dto.date); const start_time = toHHmmFromString(dto.start_time); const end_time = toHHmmFromString(dto.end_time); - - return { success: true, data: {date, start_time, end_time, bank_code_id: bank_code_id.data} }; + + return { success: true, data: { date, start_time, end_time, bank_code_id: bank_code_id.data } }; } } diff --git a/src/time-and-attendance/shifts/services/shifts-update-delete.service.ts b/src/time-and-attendance/shifts/services/shifts-update-delete.service.ts index 47344bf..85f4d87 100644 --- a/src/time-and-attendance/shifts/services/shifts-update-delete.service.ts +++ b/src/time-and-attendance/shifts/services/shifts-update-delete.service.ts @@ -3,10 +3,10 @@ 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/common/mappers/timesheet.mapper"; import { toDateFromString, toStringFromHHmm, toStringFromDate, toHHmmFromString, overlaps } from "src/common/utils/date-utils"; +import { ShiftDto } from "src/time-and-attendance/shifts/dtos/shift-create.dto"; @Injectable() export class ShiftsUpdateDeleteService { @@ -67,18 +67,12 @@ export class ShiftsUpdateDeleteService { where: { id: dto.id, timesheet_id: timesheet.data.id }, select: shift_select, }); - if (!original) return { success: false, error: `Shift with id: ${dto.id} not found` }; + if (!original) return { success: false, error: `SHIFT_NOT_FOUND` }; //transform string format to date and HHmm const normed_shift = await this.normalizeShiftDto(dto); - if (!normed_shift.success) return { success: false, error: 'An error occured during normalization' } - if (normed_shift.data.end_time <= normed_shift.data.start_time) return { - success: false, - error: `INVALID_SHIFT - ` - + `start_time: ${toStringFromHHmm(normed_shift.data.start_time)},` - + `end_time: ${toStringFromHHmm(normed_shift.data.end_time)},` - + `date: ${toStringFromDate(normed_shift.data.date)}.` - }; + if (!normed_shift.success) return { success: false, error: normed_shift.error } + if (normed_shift.data.end_time <= normed_shift.data.start_time) return { success: false, error: `INVALID_SHIFT` }; //finds bank_code_id using the type const bank_code = await this.typeResolver.findBankCodeIDByType(dto.type); @@ -98,7 +92,7 @@ export class ShiftsUpdateDeleteService { }, select: shift_select, }); - if (!updated) return { success: false, error: ' An error occured during update, Invalid Datas' }; + if (!updated) return { success: false, error: 'INVALID_SHIFT' }; // builds an object to return for display in the frontend const shift: ShiftDto = { @@ -115,7 +109,7 @@ export class ShiftsUpdateDeleteService { return { success: true, data: shift }; } catch (error) { - return { success: false, error: `An error occured during update. Invalid Data` }; + return { success: false, error: `INVALID_SHIFT` }; } } @@ -131,13 +125,13 @@ export class ShiftsUpdateDeleteService { where: { id: shift_id }, select: { id: true, date: true, timesheet_id: true }, }); - if (!shift) return { success: false, error: `shift with id ${shift_id} not found ` }; + if (!shift) return { success: false, error: `SHIFT_NOT_FOUND` }; await tx.shifts.delete({ where: { id: shift_id } }); return { success: true, data: shift.id }; }); } catch (error) { - return { success: false, error: `INVALID_SHIFT, shift with id ${shift_id} not found` } + return { success: false, error: `SHIFT_NOT_FOUND` } } } @@ -146,7 +140,7 @@ export class ShiftsUpdateDeleteService { //_________________________________________________________________ private normalizeShiftDto = async (dto: ShiftDto): Promise> => { const bank_code_id = await this.typeResolver.findBankCodeIDByType(dto.type); - if (!bank_code_id.success) return { success: false, error: 'Bank_code not found' } + if (!bank_code_id.success) return { success: false, error: 'INVALID_SHIFT' } return { success: true, data: { @@ -163,21 +157,13 @@ export class ShiftsUpdateDeleteService { for (let j = i + 1; j < shifts.length; j++) { const shift_a = shifts[i]; const shift_b = shifts[j]; - + if (shift_a.date !== shift_b.date || shift_a.id === shift_b.id) continue; const has_overlap = overlaps( { start: toHHmmFromString(shift_a.start_time), end: toHHmmFromString(shift_a.end_time) }, { start: toHHmmFromString(shift_b.start_time), end: toHHmmFromString(shift_b.end_time) }, ); - if (has_overlap) { - return { - success: false, - error: `SHIFT_OVERLAP` - + `new shift: ${shift_a.start_time}–${shift_a.end_time} ` - + `existing shift: ${shift_b.start_time}–${shift_b.end_time} ` - + `date: ${shift_a.date})`, - } - } + if (has_overlap) return { success: false, error: `SHIFT_OVERLAP` }; } } return { success: true, data: undefined } diff --git a/src/time-and-attendance/shifts/shifts.module.ts b/src/time-and-attendance/shifts/shifts.module.ts index a68a467..7599e92 100644 --- a/src/time-and-attendance/shifts/shifts.module.ts +++ b/src/time-and-attendance/shifts/shifts.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; -import { ShiftController } from 'src/time-and-attendance/time-tracker/shifts/controllers/shift.controller'; -import { ShiftsCreateService } from 'src/time-and-attendance/time-tracker/shifts/services/shifts-create.service'; -import { ShiftsUpdateDeleteService } from 'src/time-and-attendance/time-tracker/shifts/services/shifts-update-delete.service'; +import { ShiftController } from 'src/time-and-attendance/shifts/controllers/shift.controller'; +import { ShiftsCreateService } from 'src/time-and-attendance/shifts/services/shifts-create.service'; +import { ShiftsUpdateDeleteService } from 'src/time-and-attendance/shifts/services/shifts-update-delete.service'; @Module({ controllers: [ShiftController], diff --git a/src/time-and-attendance/time-and-attendance.module.ts b/src/time-and-attendance/time-and-attendance.module.ts index 5bce86e..2364b9a 100644 --- a/src/time-and-attendance/time-and-attendance.module.ts +++ b/src/time-and-attendance/time-and-attendance.module.ts @@ -4,14 +4,7 @@ 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 { 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 { ShiftsCreateService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-create.service"; -import { ShiftsGetService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-get.service"; -import { ShiftsUpdateDeleteService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-update-delete.service"; + import { TimesheetController } from "src/time-and-attendance/timesheets/controllers/timesheet.controller"; import { TimesheetApprovalService } from "src/time-and-attendance/timesheets/services/timesheet-approval.service"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/timesheets/services/timesheet-get-overview.service"; @@ -26,6 +19,12 @@ import { PayPeriodsCommandService } from "src/time-and-attendance/pay-period/ser 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"; +import { SchedulePresetsApplyService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service"; +import { SchedulePresetsGetService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-get.service"; +import { ShiftController } from "src/time-and-attendance/shifts/controllers/shift.controller"; +import { ShiftsCreateService } from "src/time-and-attendance/shifts/services/shifts-create.service"; +import { ShiftsGetService } from "src/time-and-attendance/shifts/services/shifts-get.service"; +import { ShiftsUpdateDeleteService } from "src/time-and-attendance/shifts/services/shifts-update-delete.service"; @Module({ imports: [ @@ -39,7 +38,7 @@ import { CsvExportController } from "src/modules/exports/controllers/csv-exports controllers: [ TimesheetController, ShiftController, - SchedulePresetsController, + // SchedulePresetsController, ExpenseController, PayPeriodsController, CsvExportController, @@ -51,7 +50,7 @@ import { CsvExportController } from "src/modules/exports/controllers/csv-exports ShiftsCreateService, ShiftsUpdateDeleteService, ExpenseUpsertService, - SchedulePresetsUpsertService, + // SchedulePresetsUpsertService, SchedulePresetsGetService, SchedulePresetsApplyService, EmailToIdResolver, diff --git a/src/time-and-attendance/timesheets/dtos/timesheet.dto.ts b/src/time-and-attendance/timesheets/dtos/timesheet.dto.ts index 0626bae..a391684 100644 --- a/src/time-and-attendance/timesheets/dtos/timesheet.dto.ts +++ b/src/time-and-attendance/timesheets/dtos/timesheet.dto.ts @@ -1,67 +1,70 @@ +import { Type } from "class-transformer"; +import { IsBoolean, IsDate, IsInt, IsOptional, IsString } from "class-validator"; + export class TimesheetEntity { - id: number; - employee_id: number; - start_date: Date; - is_approved: boolean; + @IsInt() id: number; + @IsInt() employee_id: number; + @IsDate() start_date: Date; + @IsBoolean() is_approved: boolean; } export class Timesheets { - employee_fullname: string; - timesheets: Timesheet[]; + @IsString() employee_fullname: string; + @Type(() => Timesheet) timesheets: Timesheet[]; } export class Timesheet { - timesheet_id: number; - is_approved: boolean; - days: TimesheetDay[]; - weekly_hours: TotalHours[]; - weekly_expenses: TotalExpenses[]; + @IsInt() timesheet_id: number; + @IsBoolean() is_approved: boolean; + @Type(() => TimesheetDay) days: TimesheetDay[]; + @Type(() => TotalHours) weekly_hours: TotalHours[]; + @Type(() => TotalExpenses) weekly_expenses: TotalExpenses[]; } export class TimesheetDay { - date: string; - shifts: Shift[]; - expenses: Expense[]; - daily_hours: TotalHours[]; - daily_expenses: TotalExpenses[]; + @IsString() date: string; + @Type(() => Shift) shifts: Shift[]; + @Type(() => Expense) expenses: Expense[]; + @Type(() => TotalHours) daily_hours: TotalHours[]; + @Type(() => TotalExpenses) daily_expenses: TotalExpenses[]; } export class TotalHours { - regular: number; - evening: number; - emergency: number; - overtime: number; - vacation: number; - holiday: number; - sick: number; + @Type(() => Number) regular: number; + @Type(() => Number) evening: number; + @Type(() => Number) emergency: number; + @Type(() => Number) overtime: number; + @Type(() => Number) vacation: number; + @Type(() => Number) holiday: number; + @Type(() => Number) sick: number; } export class TotalExpenses { - expenses: number; - per_diem: number; - on_call: number; - mileage: number; + @Type(() => Number) expenses: number; + @Type(() => Number) per_diem: number; + @Type(() => Number) on_call: number; + @Type(() => Number) mileage: number; } export class Shift { - timesheet_id: number; - date: string; - start_time: string; - end_time: string; - type: string; - is_remote: boolean; - is_approved: boolean; - shift_id?: number | null; - comment?: string | null; + @IsInt() timesheet_id: number; + @IsString() date: string; + @IsString() start_time: string; + @IsString() end_time: string; + @IsString() type: string; + @IsBoolean() is_remote: boolean; + @IsBoolean() is_approved: boolean; + @IsInt() @IsOptional() shift_id?: number | null; + @IsString() @IsOptional() comment?: string | null; } export class Expense { - date: string; - is_approved: boolean; - type: string; - comment: string; - amount?: number; - mileage?: number; - attachment?: string; - id?: number | null; - supervisor_comment?: string | null; + @IsString() date: string; + @IsBoolean() is_approved: boolean; + @IsString() type: string; + @IsString() comment: string; + @Type(() => Number) @IsOptional() amount?: number; + @Type(() => Number) @IsOptional() mileage?: number; + @IsString() @IsOptional() attachment?: string; + @IsOptional() @IsInt() id?: number | null; + @IsString() @IsOptional() supervisor_comment?: string | null; } \ No newline at end of file diff --git a/src/time-and-attendance/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/timesheets/services/timesheet-get-overview.service.ts index 9a311cc..2edddaa 100644 --- a/src/time-and-attendance/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/timesheets/services/timesheet-get-overview.service.ts @@ -39,11 +39,11 @@ export class GetTimesheetsOverviewService { try { //find period using year and period_no const period = await this.prisma.payPeriods.findFirst({ where: { pay_year, pay_period_no } }); - if (!period) return { success: false, error: `Pay period ${pay_year}-${pay_period_no} not found` }; + if (!period) return { success: false, error: `PAY_PERIOD_NOT_FOUND` }; //fetch the employee_id using the email const employee_id = await this.emailResolver.findIdByEmail(email); - if (!employee_id.success) return { success: false, error: `employee with email: ${email} not found` + employee_id.error } + if (!employee_id.success) return { success: false, error: employee_id.error } //loads the timesheets related to the fetched pay-period let rows = await this.loadTimesheets(employee_id.data, period.period_start, period.period_end); @@ -71,7 +71,7 @@ export class GetTimesheetsOverviewService { where: { id: employee_id.data }, include: { user: true }, }); - if (!employee) return { success: false, error: `Employee #${employee_id} not found` } + if (!employee) return { success: false, error: `EMPLOYEE_NOT_FOUND` } //builds employee full name const user = employee.user; @@ -80,11 +80,11 @@ export class GetTimesheetsOverviewService { //maps all timesheet's infos const timesheets = await Promise.all(rows.map((timesheet) => this.mapOneTimesheet(timesheet))); - if (!timesheets) return { success: false, error: 'an error occured during the mapping of a timesheet' } + if (!timesheets) return { success: false, error: 'INVALID_TIMESHEET' } return { success: true, data: { employee_fullname, timesheets } }; } catch (error) { - return { success: false, error: 'timesheet failed to load: ' + pay_year + pay_period_no } + return { success: false, error: 'TIMESHEET_NOT_FOUND' } } }