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 0945443..f52fb0d 100644 --- a/src/time-and-attendance/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/expenses/services/expense-upsert.service.ts @@ -90,7 +90,6 @@ export class ExpenseUpsertService { }); if (!timesheet) return { success: false, error: `Timesheet ${dto.timesheet_id} not found` } - //checks for modifications const data: Prisma.ExpensesUpdateInput = { ...normed_expense.data, 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 9c0327d..d7c25be 100644 --- a/src/time-and-attendance/pay-period/pay-periods.module.ts +++ b/src/time-and-attendance/pay-period/pay-periods.module.ts @@ -2,7 +2,7 @@ import { PayPeriodsQueryService } from "./services/pay-periods-query.service"; import { PayPeriodsController } from "./controllers/pay-periods.controller"; import { Module } from "@nestjs/common"; import { PayPeriodsCommandService } from "src/time-and-attendance/pay-period/services/pay-periods-command.service"; -import { TimesheetsModule } from "src/time-and-attendance/time-tracker/timesheets/timesheets.module"; +import { TimesheetsModule } from "src/time-and-attendance/timesheets/timesheets.module"; import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; @Module({ 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 ede4caa..6022635 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 @@ -2,7 +2,7 @@ import { BadRequestException, ForbiddenException, Injectable, NotFoundException import { PrismaService } from "src/prisma/prisma.service"; import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto"; import { PayPeriodsQueryService } from "./pay-periods-query.service"; -import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service"; +import { TimesheetApprovalService } from "src/time-and-attendance/timesheets/services/timesheet-approval.service"; @Injectable() export class PayPeriodsCommandService { diff --git a/src/time-and-attendance/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/schedule-presets/controller/schedule-presets.controller.ts new file mode 100644 index 0000000..1bc6630 --- /dev/null +++ b/src/time-and-attendance/schedule-presets/controller/schedule-presets.controller.ts @@ -0,0 +1,64 @@ +// import { Controller, Param, Query, Body, Get, Post, ParseIntPipe, Delete, Patch, Req } from "@nestjs/common"; +// import { RolesAllowed } from "src/common/decorators/roles.decorators"; +// import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes"; +// import { SchedulePresetsDto } from "src/time-and-attendance/schedule-presets/dtos/create-schedule-presets.dto"; +// import { SchedulePresetsUpdateDto } from "src/time-and-attendance/schedule-presets/dtos/update-schedule-presets.dto"; +// 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 { SchedulePresetsUpsertService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-upsert.service"; + +// @Controller('schedule-presets') +// @RolesAllowed(...GLOBAL_CONTROLLER_ROLES) +// export class SchedulePresetsController { +// constructor( +// private readonly upsertService: SchedulePresetsUpsertService, +// private readonly getService: SchedulePresetsGetService, +// private readonly applyPresetsService: SchedulePresetsApplyService, +// ) { } + +// //used to create a schedule preset +// @Post('create') +// @RolesAllowed(...MANAGER_ROLES) +// async createPreset(@Req() req, @Body() dto: SchedulePresetsDto) { +// const email = req.user?.email; +// return await this.upsertService.createPreset(email, dto); +// } + +// // //used to update an already existing schedule preset +// // @Patch('update/:preset_id') +// // @RolesAllowed(...MANAGER_ROLES) +// // async updatePreset( +// // @Param('preset_id', ParseIntPipe) preset_id: number, +// // @Body() dto: SchedulePresetsUpdateDto +// // ) { +// // return await this.upsertService.updatePreset(preset_id, dto); +// // } + +// //used to delete a schedule preset +// @Delete('delete/:preset_id') +// @RolesAllowed(...MANAGER_ROLES) +// async deletePreset( +// @Param('preset_id', ParseIntPipe) preset_id: number) { +// return await this.upsertService.deletePreset(preset_id); +// } + + +// //used to show the list of available schedule presets +// @Get('find-list') +// @RolesAllowed(...MANAGER_ROLES) +// async findListById(@Req() req) { +// const email = req.user?.email; +// return this.getService.getSchedulePresets(email); +// } + +// //used to apply a preset to a timesheet +// @Post('apply-presets') +// async applyPresets( +// @Req() req, +// @Body('preset') preset_id: number, +// @Body('start') start_date: string +// ) { +// const email = req.user?.email; +// return this.applyPresetsService.applyToTimesheet(email, preset_id, start_date); +// } +// } \ No newline at end of file diff --git a/src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts b/src/time-and-attendance/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts similarity index 100% rename from src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts rename to src/time-and-attendance/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts diff --git a/src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts b/src/time-and-attendance/schedule-presets/dtos/create-schedule-presets.dto.ts similarity index 100% rename from src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts rename to src/time-and-attendance/schedule-presets/dtos/create-schedule-presets.dto.ts diff --git a/src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts b/src/time-and-attendance/schedule-presets/dtos/update-schedule-presets.dto.ts similarity index 100% rename from src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts rename to src/time-and-attendance/schedule-presets/dtos/update-schedule-presets.dto.ts diff --git a/src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts b/src/time-and-attendance/schedule-presets/schedule-presets.module.ts similarity index 100% rename from src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts rename to src/time-and-attendance/schedule-presets/schedule-presets.module.ts diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts b/src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service.ts similarity index 98% rename from src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts rename to src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service.ts index a564ae2..bc9a51e 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts +++ b/src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service.ts @@ -1,4 +1,4 @@ -import { Injectable, BadRequestException, NotFoundException, ConflictException } from "@nestjs/common"; +import { Injectable } from "@nestjs/common"; import { Weekday, Prisma } from "@prisma/client"; import { DATE_ISO_FORMAT, WEEKDAY } from "src/common/utils/constants.utils"; import { PrismaService } from "src/prisma/prisma.service"; diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts b/src/time-and-attendance/schedule-presets/services/schedule-presets-get.service.ts similarity index 100% rename from src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts rename to src/time-and-attendance/schedule-presets/services/schedule-presets-get.service.ts diff --git a/src/time-and-attendance/schedule-presets/services/schedule-presets-upsert.service.ts b/src/time-and-attendance/schedule-presets/services/schedule-presets-upsert.service.ts new file mode 100644 index 0000000..7644f90 --- /dev/null +++ b/src/time-and-attendance/schedule-presets/services/schedule-presets-upsert.service.ts @@ -0,0 +1,227 @@ +// import { Injectable, BadRequestException, NotFoundException, ConflictException } from "@nestjs/common"; +// import { Prisma, Weekday } from "@prisma/client"; +// import { PrismaService } from "src/prisma/prisma.service"; +// import { BankCodesResolver } from "src/common/mappers/bank-type-id.mapper"; +// 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"; +// import { SchedulePresetsDto } from "src/time-and-attendance/schedule-presets/dtos/create-schedule-presets.dto"; + +// @Injectable() +// export class SchedulePresetsUpsertService { +// constructor( +// private readonly prisma: PrismaService, +// private readonly typeResolver: BankCodesResolver, +// private readonly emailResolver: EmailToIdResolver, +// ) { } +// //_________________________________________________________________ +// // CREATE +// //_________________________________________________________________ +// async createPreset(email: string, dto: SchedulePresetsDto): Promise> { +// try { +// const shifts_data = await this.normalizePresetShifts(dto); +// if (!shifts_data.success) return { success: false, error: `Employee with email: ${email} or dto not found` }; + +// const employee_id = await this.emailResolver.findIdByEmail(email); +// if (!employee_id.success) return { success: false, error: employee_id.error }; + +// const created = await this.prisma.$transaction(async (tx) => { +// if (dto.is_default) { +// await tx.schedulePresets.updateMany({ +// where: { is_default: true, employee_id: employee_id.data }, +// data: { is_default: false }, +// }); +// await tx.schedulePresets.create({ +// data: { +// id: dto.id, +// employee_id: employee_id.data, +// name: dto.name, +// is_default: !!dto.is_default, +// shifts: { create: shifts_data.data }, +// }, +// }); +// return { success: true, data: created } +// } +// }); +// return { success: true, data: created } +// } catch (error) { +// return { success: false, error: ' An error occured during create. Invalid Schedule data' }; +// } +// } + +// //_________________________________________________________________ +// // UPDATE +// //_________________________________________________________________ +// async updatePreset(preset_id: number, dto: SchedulePresetsDto): Promise> { +// try { +// const existing = await this.prisma.schedulePresets.findFirst({ +// where: { id: preset_id }, +// select: { +// id: true, +// is_default: true, +// employee_id: true, +// }, +// }); +// if (!existing) return { success: false, error: `Preset "${dto.name}" not found` }; + +// const shifts_data = await this.normalizePresetShifts(dto); +// if (!shifts_data.success) return { success: false, error: 'An error occured during normalization' } + +// await this.prisma.$transaction(async (tx) => { +// if (typeof dto.is_default === 'boolean') { +// if (dto.is_default) { +// await tx.schedulePresets.updateMany({ +// where: { +// employee_id: existing.employee_id, +// is_default: true, +// NOT: { id: existing.id }, +// }, +// data: { is_default: false }, +// }); +// } +// await tx.schedulePresets.update({ +// where: { id: existing.id }, +// data: { +// is_default: dto.is_default, +// name: dto.name, +// }, +// }); +// } +// if (shifts_data.data.length <= 0) return { success: false, error: 'Preset shifts to update not found' }; + +// await tx.schedulePresetShifts.deleteMany({ where: { preset_id: existing.id } }); + +// try { +// const create_many_data: Result = +// shifts_data.data.map((shift) => { +// if (!shift.bank_code || !('connect' in shift.bank_code) || typeof shift.bank_code.connect?.id !== 'number') { +// return { success: false, error: `Bank code is required for updates( ${shift.week_day}, ${shift.sort_order})`} +// } +// const bank_code_id = shift.bank_code.connect.id; +// return { +// preset_id: existing.id, +// week_day: shift.week_day, +// sort_order: shift.sort_order, +// start_time: shift.start_time, +// end_time: shift.end_time, +// is_remote: shift.is_remote ?? false, +// bank_code_id: bank_code_id, +// }; +// }); +// if(!create_many_data.success) return { success: false, error: 'Invalid data'} +// await tx.schedulePresetShifts.createMany({ data: create_many_data.data }); + +// return { success: true, data: create_many_data } +// } catch (error) { +// return { success: false, error: 'An error occured. Invalid data detected. ' }; +// } +// }); + +// const saved = await this.prisma.schedulePresets.findUnique({ +// where: { id: existing.id }, +// include: { +// shifts: { +// orderBy: [{ week_day: 'asc' }, { sort_order: 'asc' }], +// include: { bank_code: { select: { type: true } } }, +// } +// }, +// }); +// if (!saved) return { success: false, error: `Preset with id: ${existing.id} not found` }; + +// const response_dto: SchedulePresetsDto = { +// id: saved.id, +// name: saved.name, +// is_default: saved.is_default, +// preset_shifts: saved.shifts.map((shift) => ({ +// preset_id: shift.preset_id, +// week_day: shift.week_day, +// sort_order: shift.sort_order, +// type: shift.bank_code.type, +// start_time: toHHmmFromDate(shift.start_time), +// end_time: toHHmmFromDate(shift.end_time), +// is_remote: shift.is_remote, +// })), +// }; + +// return { success: true, data: response_dto }; +// } catch (error) { +// return { success: false, error: 'An error occured during update. Invalid data' } +// } +// } + +// //_________________________________________________________________ +// // DELETE +// //_________________________________________________________________ +// async deletePreset(preset_id: number): Promise> { +// try { +// await this.prisma.$transaction(async (tx) => { +// const preset = await tx.schedulePresets.findFirst({ +// where: { id: preset_id }, +// select: { id: true }, +// }); +// if (!preset) return { success: false, error: `Preset with id ${preset_id} not found` }; +// await tx.schedulePresets.delete({ where: { id: preset_id } }); + +// return { success: true }; +// }); +// return { success: true, data: preset_id }; + +// } catch (error) { +// return { success: false, error: `Preset schedule with id ${preset_id} not found` }; +// } +// } + +// //PRIVATE HELPERS + +// //resolve bank_code_id using type and convert hours to TIME and valid shifts end/start +// private async normalizePresetShifts( +// dto: SchedulePresetsDto +// ): Promise> { +// if (!dto.preset_shifts?.length) return { success: false, error: `Empty or preset shifts not found` } + +// const types = Array.from(new Set(dto.preset_shifts.map((shift) => shift.type))); +// const bank_code_set = new Map(); + +// for (const type of types) { +// const bank_code = await this.typeResolver.findIdAndModifierByType(type); +// if (!bank_code.success) return { success: false, error: 'Bank_code not found' } +// bank_code_set.set(type, bank_code.data.id); +// } + +// const pair_set = new Set(); +// for (const shift of dto.preset_shifts) { +// const key = `${shift.week_day}:${shift.sort_order}`; +// if (pair_set.has(key)) { +// return { success: false, error: `Duplicate shift for day/order (${shift.week_day}, ${shift.sort_order})` } +// } +// pair_set.add(key); +// } + +// const items = await dto.preset_shifts.map((shift) => { +// try { +// const bank_code_id = bank_code_set.get(shift.type); +// if (!bank_code_id) return { success: false, error: `Bank code not found for type ${shift.type}` } +// if (!shift.start_time || !shift.end_time) { +// return { success: false, error: `start_time and end_time are required for (${shift.week_day}, ${shift.sort_order})` } +// } +// const start = toDateFromString(shift.start_time); +// const end = toDateFromString(shift.end_time); +// if (end.getTime() <= start.getTime()) { +// return { success: false, error: `end_time must be > start_time ( day: ${shift.week_day}, order: ${shift.sort_order})` } +// } +// return { +// sort_order: shift.sort_order, +// start_time: start, +// end_time: end, +// is_remote: !!shift.is_remote, +// week_day: shift.week_day as Weekday, +// bank_code: { connect: { id: bank_code_id } }, +// } + +// } catch (error) { +// return { success: false, error: '' } +// } +// }); +// return { success: true, data: items}; +// } +// } diff --git a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/shifts/controllers/shift.controller.ts similarity index 100% rename from src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts rename to src/time-and-attendance/shifts/controllers/shift.controller.ts diff --git a/src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto.ts b/src/time-and-attendance/shifts/dtos/shift-create.dto.ts similarity index 100% rename from src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto.ts rename to src/time-and-attendance/shifts/dtos/shift-create.dto.ts diff --git a/src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto.ts b/src/time-and-attendance/shifts/dtos/shift-get.dto.ts similarity index 100% rename from src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto.ts rename to src/time-and-attendance/shifts/dtos/shift-get.dto.ts diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-archival.service.ts b/src/time-and-attendance/shifts/services/shifts-archival.service.ts similarity index 100% rename from src/time-and-attendance/time-tracker/shifts/services/shifts-archival.service.ts rename to src/time-and-attendance/shifts/services/shifts-archival.service.ts diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-create.service.ts b/src/time-and-attendance/shifts/services/shifts-create.service.ts similarity index 100% rename from src/time-and-attendance/time-tracker/shifts/services/shifts-create.service.ts rename to src/time-and-attendance/shifts/services/shifts-create.service.ts diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts b/src/time-and-attendance/shifts/services/shifts-get.service.ts similarity index 100% rename from src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts rename to src/time-and-attendance/shifts/services/shifts-get.service.ts diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-update-delete.service.ts b/src/time-and-attendance/shifts/services/shifts-update-delete.service.ts similarity index 100% rename from src/time-and-attendance/time-tracker/shifts/services/shifts-update-delete.service.ts rename to src/time-and-attendance/shifts/services/shifts-update-delete.service.ts diff --git a/src/time-and-attendance/time-tracker/shifts/shifts.module.ts b/src/time-and-attendance/shifts/shifts.module.ts similarity index 100% rename from src/time-and-attendance/time-tracker/shifts/shifts.module.ts rename to src/time-and-attendance/shifts/shifts.module.ts diff --git a/src/time-and-attendance/time-and-attendance.module.ts b/src/time-and-attendance/time-and-attendance.module.ts index 0ff6657..5bce86e 100644 --- a/src/time-and-attendance/time-and-attendance.module.ts +++ b/src/time-and-attendance/time-and-attendance.module.ts @@ -12,10 +12,10 @@ import { ShiftController } from "src/time-and-attendance/time-tracker/shifts/con 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/time-tracker/timesheets/controllers/timesheet.controller"; -import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service"; -import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service"; -import { TimesheetsModule } from "src/time-and-attendance/time-tracker/timesheets/timesheets.module"; +import { 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"; +import { TimesheetsModule } from "src/time-and-attendance/timesheets/timesheets.module"; 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"; diff --git a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts deleted file mode 100644 index a4a7b9e..0000000 --- a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Controller, Param, Query, Body, Get, Post, ParseIntPipe, Delete, Patch, Req } from "@nestjs/common"; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; -import { SchedulePresetsUpdateDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto"; -import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; -import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; -import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; -import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes"; - -@Controller('schedule-presets') -@RolesAllowed(...GLOBAL_CONTROLLER_ROLES) -export class SchedulePresetsController { - constructor( - private readonly upsertService: SchedulePresetsUpsertService, - private readonly getService: SchedulePresetsGetService, - private readonly applyPresetsService: SchedulePresetsApplyService, - ) { } - - //used to create a schedule preset - @Post('create') - @RolesAllowed(...MANAGER_ROLES) - async createPreset(@Req() req, @Body() dto: SchedulePresetsDto) { - const email = req.user?.email; - return await this.upsertService.createPreset(email, dto); - } - - //used to update an already existing schedule preset - @Patch('update/:preset_id') - @RolesAllowed(...MANAGER_ROLES) - async updatePreset( - @Param('preset_id', ParseIntPipe) preset_id: number, - @Body() dto: SchedulePresetsUpdateDto - ) { - return await this.upsertService.updatePreset(preset_id, dto); - } - - //used to delete a schedule preset - @Delete('delete/:preset_id') - @RolesAllowed(...MANAGER_ROLES) - async deletePreset( - @Param('preset_id', ParseIntPipe) preset_id: number) { - return await this.upsertService.deletePreset(preset_id); - } - - - //used to show the list of available schedule presets - @Get('find-list') - @RolesAllowed(...MANAGER_ROLES) - async findListById(@Req() req) { - const email = req.user?.email; - return this.getService.getSchedulePresets(email); - } - - //used to apply a preset to a timesheet - @Post('apply-presets') - async applyPresets( - @Req() req, - @Body('preset') preset_id: number, - @Body('start') start_date: string - ) { - const email = req.user?.email; - return this.applyPresetsService.applyToTimesheet(email, preset_id, start_date); - } -} \ No newline at end of file diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts deleted file mode 100644 index 01717e9..0000000 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { Injectable, BadRequestException, NotFoundException, ConflictException } from "@nestjs/common"; -import { Prisma, Weekday } from "@prisma/client"; -import { PrismaService } from "src/prisma/prisma.service"; -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/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 { - constructor( - private readonly prisma: PrismaService, - private readonly typeResolver: BankCodesResolver, - private readonly emailResolver: EmailToIdResolver, - ) { } - //_________________________________________________________________ - // CREATE - //_________________________________________________________________ - async createPreset(email: string, dto: SchedulePresetsDto): Promise> { - try { - const shifts_data = await this.normalizePresetShifts(dto); - if (!shifts_data.success) return { success: false, error: `Employee with email: ${email} or dto not found` }; - - const employee_id = await this.emailResolver.findIdByEmail(email); - if (!employee_id.success) return { success: false, error: employee_id.error }; - - const created = await this.prisma.$transaction(async (tx) => { - if (dto.is_default) { - await tx.schedulePresets.updateMany({ - where: { is_default: true, employee_id: employee_id.data }, - data: { is_default: false }, - }); - await tx.schedulePresets.create({ - data: { - id: dto.id, - employee_id: employee_id.data, - name: dto.name, - is_default: !!dto.is_default, - shifts: { create: shifts_data.data }, - }, - }); - return { success: true, data: created } - } - }); - return { success: true, data: created } - } catch (error) { - return { success: false, error: ' An error occured during create. Invalid Schedule data' }; - } - } - - //_________________________________________________________________ - // UPDATE - //_________________________________________________________________ - async updatePreset(preset_id: number, dto: SchedulePresetsDto): Promise> { - try { - const existing = await this.prisma.schedulePresets.findFirst({ - where: { id: preset_id }, - select: { - id: true, - is_default: true, - employee_id: true, - }, - }); - if (!existing) return { success: false, error: `Preset "${dto.name}" not found` }; - - const shifts_data = await this.normalizePresetShifts(dto); - if(!shifts_data.success) return { success: false, error: 'An error occured during normalization'} - - await this.prisma.$transaction(async (tx) => { - if (typeof dto.is_default === 'boolean') { - if (dto.is_default) { - await tx.schedulePresets.updateMany({ - where: { - employee_id: existing.employee_id, - is_default: true, - NOT: { id: existing.id }, - }, - data: { is_default: false }, - }); - } - await tx.schedulePresets.update({ - where: { id: existing.id }, - data: { - is_default: dto.is_default, - name: dto.name, - }, - }); - } - if (shifts_data.data.length <= 0) return { success: false, error: 'Preset shifts to update not found' }; - - await tx.schedulePresetShifts.deleteMany({ where: { preset_id: existing.id } }); - - // try { - // const create_many_data: Result = - // shifts_data.data.map((shift) => { - // if (!shift.bank_code || !('connect' in shift.bank_code) || typeof shift.bank_code.connect?.id !== 'number') { - // return { success: false, error: `Bank code is required for updates( ${shift.week_day}, ${shift.sort_order})`} - // } - // const bank_code_id = shift.bank_code.connect.id; - // return { - // preset_id: existing.id, - // week_day: shift.week_day, - // sort_order: shift.sort_order, - // start_time: shift.start_time, - // end_time: shift.end_time, - // is_remote: shift.is_remote ?? false, - // bank_code_id: bank_code_id, - // }; - // }); - // if(!create_many_data.success) return { success: false, error: 'Invalid data'} - // await tx.schedulePresetShifts.createMany({ data: create_many_data.data }); - - // return { success: true, data: create_many_data } - // } catch (error) { - // return { success: false, error: 'An error occured. Invalid data detected. ' }; - // } - }); - - const saved = await this.prisma.schedulePresets.findUnique({ - where: { id: existing.id }, - include: { - shifts: { - orderBy: [{ week_day: 'asc' }, { sort_order: 'asc' }], - include: { bank_code: { select: { type: true } } }, - } - }, - }); - if (!saved) return { success: false, error: `Preset with id: ${existing.id} not found` }; - - const response_dto: SchedulePresetsDto = { - id: saved.id, - name: saved.name, - is_default: saved.is_default, - preset_shifts: saved.shifts.map((shift) => ({ - preset_id: shift.preset_id, - week_day: shift.week_day, - sort_order: shift.sort_order, - type: shift.bank_code.type, - start_time: toHHmmFromDate(shift.start_time), - end_time: toHHmmFromDate(shift.end_time), - is_remote: shift.is_remote, - })), - }; - - return { success: true, data: response_dto }; - } catch (error) { - return { success: false, error: 'An error occured during update. Invalid data' } - } - } - - //_________________________________________________________________ - // DELETE - //_________________________________________________________________ - async deletePreset(preset_id: number): Promise> { - try { - await this.prisma.$transaction(async (tx) => { - const preset = await tx.schedulePresets.findFirst({ - where: { id: preset_id }, - select: { id: true }, - }); - if (!preset) return { success: false, error: `Preset with id ${preset_id} not found` }; - await tx.schedulePresets.delete({ where: { id: preset_id } }); - - return { success: true }; - }); - return { success: true, data: preset_id }; - - } catch (error) { - return { success: false, error: `Preset schedule with id ${preset_id} not found` }; - } - } - - //PRIVATE HELPERS - - //resolve bank_code_id using type and convert hours to TIME and valid shifts end/start - private async normalizePresetShifts( - dto: SchedulePresetsDto - ): Promise> { - if (!dto.preset_shifts?.length) throw new NotFoundException(`Empty or preset shifts not found`); - - const types = Array.from(new Set(dto.preset_shifts.map((shift) => shift.type))); - const bank_code_set = new Map(); - - for (const type of types) { - const bank_code = await this.typeResolver.findIdAndModifierByType(type); - if (!bank_code.success) return { success: false, error: 'Bank_code not found' } - bank_code_set.set(type, bank_code.data.id); - } - - const pair_set = new Set(); - for (const shift of dto.preset_shifts) { - const key = `${shift.week_day}:${shift.sort_order}`; - if (pair_set.has(key)) { - throw new ConflictException(`Duplicate shift for day/order (${shift.week_day}, ${shift.sort_order})`); - } - pair_set.add(key); - } - - const items: Prisma.SchedulePresetShiftsCreateWithoutPresetInput[] = dto.preset_shifts.map((shift) => { - const bank_code_id = bank_code_set.get(shift.type); - if (!bank_code_id) throw new NotFoundException(`Bank code not found for type ${shift.type}`); - if (!shift.start_time || !shift.end_time) { - throw new BadRequestException(`start_time and end_time are required for (${shift.week_day}, ${shift.sort_order})`); - } - const start = toDateFromString(shift.start_time); - const end = toDateFromString(shift.end_time); - if (end.getTime() <= start.getTime()) { - throw new ConflictException(`end_time must be > start_time ( day: ${shift.week_day}, order: ${shift.sort_order})`); - } - - return { - week_day: shift.week_day as Weekday, - sort_order: shift.sort_order, - bank_code: { connect: { id: bank_code_id } }, - start_time: start, - end_time: end, - is_remote: !!shift.is_remote, - }; - }); - return { success: true, data: items }; - } -} diff --git a/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/timesheets/controllers/timesheet.controller.ts similarity index 91% rename from src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts rename to src/time-and-attendance/timesheets/controllers/timesheet.controller.ts index ea9b181..b8e6e7b 100644 --- a/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/timesheets/controllers/timesheet.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Req, UnauthorizedException } from "@nestjs/common"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service"; -import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service"; +import { GetTimesheetsOverviewService } from "src/time-and-attendance/timesheets/services/timesheet-get-overview.service"; +import { TimesheetApprovalService } from "src/time-and-attendance/timesheets/services/timesheet-approval.service"; import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes"; diff --git a/src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto.ts b/src/time-and-attendance/timesheets/dtos/timesheet.dto.ts similarity index 100% rename from src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto.ts rename to src/time-and-attendance/timesheets/dtos/timesheet.dto.ts diff --git a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts b/src/time-and-attendance/timesheets/services/timesheet-approval.service.ts similarity index 100% rename from src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts rename to src/time-and-attendance/timesheets/services/timesheet-approval.service.ts diff --git a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service.ts b/src/time-and-attendance/timesheets/services/timesheet-archive.service.ts similarity index 100% rename from src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service.ts rename to src/time-and-attendance/timesheets/services/timesheet-archive.service.ts diff --git a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/timesheets/services/timesheet-get-overview.service.ts similarity index 99% rename from src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts rename to src/time-and-attendance/timesheets/services/timesheet-get-overview.service.ts index 9f5481a..9a311cc 100644 --- a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/timesheets/services/timesheet-get-overview.service.ts @@ -3,7 +3,7 @@ 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/common/mappers/email-id.mapper"; -import { Timesheet, Timesheets } from "src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto"; +import { Timesheet, Timesheets } from "src/time-and-attendance/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"; diff --git a/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts b/src/time-and-attendance/timesheets/timesheets.module.ts similarity index 73% rename from src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts rename to src/time-and-attendance/timesheets/timesheets.module.ts index 6590750..78457f4 100644 --- a/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts +++ b/src/time-and-attendance/timesheets/timesheets.module.ts @@ -1,8 +1,8 @@ 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 { 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'; import { EmailToIdResolver } from 'src/common/mappers/email-id.mapper'; @Module({