diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index cb484fd..d3f50c3 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -514,7 +514,7 @@ ] } }, - "/employees": { + "/employees/create": { "post": { "operationId": "EmployeesController_createEmployee", "parameters": [], @@ -538,6 +538,20 @@ ] } }, + "/employees/update": { + "patch": { + "operationId": "EmployeesController_updateEmployee", + "parameters": [], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Employees" + ] + } + }, "/preferences/update": { "patch": { "operationId": "PreferencesController_updatePreferences", diff --git a/src/identity-and-account/employees/controllers/employees.controller.ts b/src/identity-and-account/employees/controllers/employees.controller.ts index 4ea15d1..dc88bac 100644 --- a/src/identity-and-account/employees/controllers/employees.controller.ts +++ b/src/identity-and-account/employees/controllers/employees.controller.ts @@ -1,52 +1,50 @@ -import { Controller, Get, Query, Body, Post } from "@nestjs/common"; +import { Controller, Get, Query, Body, Post, Patch } from "@nestjs/common"; import { Access } from "src/common/decorators/module-access.decorators"; import { Result } from "src/common/errors/result-error.factory"; import { EmployeeDetailedDto } from "src/identity-and-account/employees/dtos/employee-detailed.dto"; import { EmployeeDto } from "src/identity-and-account/employees/dtos/employee.dto"; -import { EmployeesService } from "src/identity-and-account/employees/services/employees.service"; +import { EmployeesGetService } from "src/identity-and-account/employees/services/employees-get.service"; import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators"; import { Modules as ModulesEnum } from ".prisma/client"; +import { EmployeesCreateService } from "src/identity-and-account/employees/services/employees-create.service"; +import { EmployeesUpdateService } from "src/identity-and-account/employees/services/employees-update.service"; @Controller('employees') export class EmployeesController { - constructor(private readonly employeesService: EmployeesService) { } + constructor( + private readonly getService: EmployeesGetService, + private readonly createService: EmployeesCreateService, + private readonly updateService: EmployeesUpdateService, + ) { } @Get('personal-profile') @ModuleAccessAllowed(ModulesEnum.personal_profile) async findOwnProfile(@Access('email') email: string): Promise, string>> { - return await this.employeesService.findOwnProfile(email); + return await this.getService.findOwnProfile(email); } @Get('profile') @ModuleAccessAllowed(ModulesEnum.employee_management) async findProfile(@Access('email') email: string, @Query('employee_email') employee_email?: string, ): Promise, string>> { - return await this.employeesService.findOneDetailedProfile(email, employee_email); + return await this.getService.findOneDetailedProfile(email, employee_email); } @Get('employee-list') @ModuleAccessAllowed(ModulesEnum.employee_list) async findListEmployees(): Promise> { - return this.employeesService.findListEmployees(); + return this.getService.findListEmployees(); } - @Post() + @Post('create') @ModuleAccessAllowed(ModulesEnum.employee_management) async createEmployee(@Body() dto: EmployeeDetailedDto): Promise> { - return await this.employeesService.createEmployee(dto); + return await this.createService.createEmployee(dto); } - // @Patch() - // async updateOrArchiveOrRestore(@Req() req, @Body() dto: UpdateEmployeeDto,) { - // // if last_work_day is set => archive the employee - // // else if employee is archived and first_work_day or last_work_day = null => restore - // //otherwise => standard update - // const email = req.user?.email; - // const result = await this.archiveService.patchEmployee(email, dto); - // if (!result) { - // throw new NotFoundException(`Employee with email: ${email} is not found in active or archive.`) - // } - // return result; - // - + @Patch('update') + @ModuleAccessAllowed(ModulesEnum.employee_management) + async updateEmployee(@Access('email') email:string, dto:EmployeeDetailedDto, employee_email?: string){ + return await this.updateService.updateEmployee(email, dto, employee_email); + } } diff --git a/src/identity-and-account/employees/employees.module.ts b/src/identity-and-account/employees/employees.module.ts index 9ad88eb..5b65c72 100644 --- a/src/identity-and-account/employees/employees.module.ts +++ b/src/identity-and-account/employees/employees.module.ts @@ -1,13 +1,21 @@ import { Module } from '@nestjs/common'; import { EmployeesController } from './controllers/employees.controller'; -import { EmployeesService } from './services/employees.service'; +import { EmployeesGetService } from './services/employees-get.service'; import { AccessGetService } from 'src/identity-and-account/user-module-access/services/module-access-get.service'; import { EmailToIdResolver } from 'src/common/mappers/email-id.mapper'; +import { EmployeesUpdateService } from 'src/identity-and-account/employees/services/employees-update.service'; +import { EmployeesCreateService } from 'src/identity-and-account/employees/services/employees-create.service'; @Module({ imports: [], controllers: [EmployeesController], - providers: [EmployeesService, AccessGetService, EmailToIdResolver], - exports: [EmployeesService ], + providers: [ + EmployeesGetService, + EmployeesUpdateService, + EmployeesCreateService, + AccessGetService, + EmailToIdResolver + ], + exports: [EmployeesGetService], }) -export class EmployeesModule {} +export class EmployeesModule { } diff --git a/src/identity-and-account/employees/services/employees-create.service.ts b/src/identity-and-account/employees/services/employees-create.service.ts new file mode 100644 index 0000000..16d2d0d --- /dev/null +++ b/src/identity-and-account/employees/services/employees-create.service.ts @@ -0,0 +1,61 @@ +import { Injectable } from "@nestjs/common"; +import { Users } from "@prisma/client"; +import { Result } from "src/common/errors/result-error.factory"; +import { toBooleanFromString } from "src/common/mappers/module-access.mapper"; +import { EmployeeDetailedDto } from "src/identity-and-account/employees/dtos/employee-detailed.dto"; +import { toCompanyCodeFromString } from "src/identity-and-account/employees/utils/employee.utils"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Injectable() +export class EmployeesCreateService { + constructor(private readonly prisma: PrismaService) { } + + async createEmployee(dto: EmployeeDetailedDto): Promise> { + const normalized_access = toBooleanFromString(dto.user_module_access); + const supervisor_id = await this.toIdFromFullName(dto.supervisor_full_name); + const company_code = toCompanyCodeFromString(dto.company_name) + await this.prisma.$transaction(async (tx) => { + const user: Users = await tx.users.create({ + data: { + first_name: dto.first_name, + last_name: dto.last_name, + email: dto.email, + phone_number: dto.phone_number, + residence: dto.residence, + user_module_access: { + create: { + dashboard: normalized_access.dashboard, + employee_list: normalized_access.employee_list, + employee_management: normalized_access.employee_management, + personal_profile: normalized_access.personal_profile, + timesheets: normalized_access.timesheets, + timesheets_approval: normalized_access.timesheets_approval, + }, + }, + }, + }); + return tx.employees.create({ + data: { + user_id: user.id, + company_code: company_code, + job_title: dto.job_title, + first_work_day: dto.first_work_day, + last_work_day: dto.last_work_day, + is_supervisor: dto.is_supervisor, + supervisor_id: supervisor_id, + }, + }); + }); + return { success: true, data: true } + } + + private toIdFromFullName = async (full_name: string) => { + const [first_name, last_name] = full_name.split(' ', 2); + let supervisor = await this.prisma.users.findFirst({ + where: { first_name, last_name }, + select: { employee: { select: { id: true } } } + }); + if (!supervisor) supervisor = null; + return supervisor?.employee?.id; + } +} \ No newline at end of file diff --git a/src/identity-and-account/employees/services/employees.service.ts b/src/identity-and-account/employees/services/employees-get.service.ts similarity index 76% rename from src/identity-and-account/employees/services/employees.service.ts rename to src/identity-and-account/employees/services/employees-get.service.ts index 0ebaed4..619474d 100644 --- a/src/identity-and-account/employees/services/employees.service.ts +++ b/src/identity-and-account/employees/services/employees-get.service.ts @@ -1,15 +1,15 @@ import { Injectable } from "@nestjs/common"; -import { Users } from "@prisma/client"; import { Result } from "src/common/errors/result-error.factory"; import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; -import { module_list, Modules, toBooleanFromString, toStringFromBoolean } from "src/common/mappers/module-access.mapper"; +import { Modules, toStringFromBoolean } from "src/common/mappers/module-access.mapper"; import { toStringFromDate } from "src/common/utils/date-utils"; import { EmployeeDetailedDto } from "src/identity-and-account/employees/dtos/employee-detailed.dto"; import { EmployeeDto } from "src/identity-and-account/employees/dtos/employee.dto"; +import { toStringFromCompanyCode } from "src/identity-and-account/employees/utils/employee.utils"; import { PrismaService } from "src/prisma/prisma.service"; @Injectable() -export class EmployeesService { +export class EmployeesGetService { constructor( private readonly prisma: PrismaService, private readonly emailResolver: EmailToIdResolver, @@ -88,11 +88,7 @@ export class EmployeesService { }); if (!existing_profile) return { success: false, error: 'EMPLOYEE_NOT_FOUND' }; - let company_name = 'Solucom'; - if (existing_profile.company_code === 271583) { - company_name = 'Targo'; - } - + const company_name = toStringFromCompanyCode(existing_profile.company_code); return { success: true, data: { first_name: existing_profile.user.first_name, @@ -159,7 +155,7 @@ export class EmployeesService { let module_access_array: Modules[] = []; if (employee.user.user_module_access) { - module_access_array = toStringFromBoolean(employee.user.user_module_access); + module_access_array = toStringFromBoolean(employee.user.user_module_access); } let company_name = 'Solucom'; @@ -186,49 +182,4 @@ export class EmployeesService { }, }; } - - async createEmployee(dto: EmployeeDetailedDto): Promise> { - let company_code = 271585; - if (dto.company_name === 'Targo') { - company_code = 271583; - } - const normalized_access = toBooleanFromString(dto.user_module_access) - - await this.prisma.$transaction(async (tx) => { - const user: Users = await tx.users.create({ - data: { - first_name: dto.first_name, - last_name: dto.last_name, - email: dto.email, - phone_number: dto.phone_number, - residence: dto.residence, - user_module_access: { - create: { - dashboard: normalized_access.dashboard, - employee_list: normalized_access.employee_list, - employee_management: normalized_access.employee_management, - personal_profile: normalized_access.personal_profile, - timesheets: normalized_access.timesheets, - timesheets_approval: normalized_access.timesheets_approval, - }, - }, - }, - }); - return tx.employees.create({ - data: { - user_id: user.id, - company_code: company_code, - job_title: dto.job_title, - first_work_day: dto.first_work_day, - last_work_day: dto.last_work_day, - is_supervisor: dto.is_supervisor, - }, - }) - }); - return { success: true, data: true } - } - - // async updateEmployeeProfile = () => { - - // } } \ No newline at end of file diff --git a/src/identity-and-account/employees/services/employees-update.service.ts b/src/identity-and-account/employees/services/employees-update.service.ts new file mode 100644 index 0000000..aca67e7 --- /dev/null +++ b/src/identity-and-account/employees/services/employees-update.service.ts @@ -0,0 +1,72 @@ +import { Injectable } from "@nestjs/common"; +import { Result } from "src/common/errors/result-error.factory"; +import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; +import { toBooleanFromString } from "src/common/mappers/module-access.mapper"; +import { EmployeeDetailedDto } from "src/identity-and-account/employees/dtos/employee-detailed.dto"; +import { toCompanyCodeFromString } from "src/identity-and-account/employees/utils/employee.utils"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Injectable() +export class EmployeesUpdateService { + constructor( + private readonly prisma: PrismaService, + private readonly emailResolver: EmailToIdResolver, + ) { } + + async updateEmployee(email: string, dto: EmployeeDetailedDto, employee_email?: string): Promise> { + const account_email = employee_email ?? email; + const user_id = await this.emailResolver.resolveUserIdWithEmail(account_email); + if (!user_id.success) return { success: false, error: 'EMPLOYEE_NOT_FOUND' } + + const company_code = toCompanyCodeFromString(dto.company_name); + const supervisor_id = await this.toIdFromFullName(dto.supervisor_full_name); + const normalized_access = await toBooleanFromString(dto.user_module_access); + + await this.prisma.$transaction(async (tx) => { + await tx.users.update({ + where: { id: user_id.data }, + data: { + first_name: dto.first_name, + last_name: dto.last_name, + email: dto.email, + phone_number: dto.phone_number, + residence: dto.residence, + user_module_access: { + update: { + dashboard: normalized_access.dashboard, + employee_list: normalized_access.employee_list, + employee_management: normalized_access.employee_management, + personal_profile: normalized_access.personal_profile, + timesheets: normalized_access.timesheets, + timesheets_approval: normalized_access.timesheets_approval, + }, + }, + }, + }); + return tx.employees.update({ + where: { user_id: user_id.data }, + data: { + company_code: company_code, + job_title: dto.job_title, + first_work_day: dto.first_work_day, + last_work_day: dto.last_work_day, + is_supervisor: dto.is_supervisor, + supervisor_id: supervisor_id, + }, + }); + + }); + return { success: true, data: true }; + + } + + private toIdFromFullName = async (full_name: string) => { + const [first_name, last_name] = full_name.split(' ', 2); + let supervisor = await this.prisma.users.findFirst({ + where: { first_name, last_name }, + select: { employee: { select: { id: true } } } + }); + if (!supervisor) supervisor = null; + return supervisor?.employee?.id; + } +} \ No newline at end of file diff --git a/src/identity-and-account/employees/utils/employee.utils.ts b/src/identity-and-account/employees/utils/employee.utils.ts index 3534f3d..79acdc4 100644 --- a/src/identity-and-account/employees/utils/employee.utils.ts +++ b/src/identity-and-account/employees/utils/employee.utils.ts @@ -6,4 +6,20 @@ export function toDateOrNull(v?: string | null): Date | null { export function toDateOrUndefined(v?: string | null): Date | undefined { const day = toDateOrNull(v ?? undefined); return day === null ? undefined : day; +} + +export function toCompanyCodeFromString(company_name: string) { + let company_code = 271585; + if (company_name === 'Targo') { + company_code = 271583; + } + return company_code; +} + +export function toStringFromCompanyCode(company_code: number) { + let company_name = 'Solucom'; + if (company_code === 271583) { + company_name = 'Targo'; + } + return company_name; } \ No newline at end of file diff --git a/src/identity-and-account/identity-and-account.module.ts b/src/identity-and-account/identity-and-account.module.ts index 076c176..b67a3f8 100644 --- a/src/identity-and-account/identity-and-account.module.ts +++ b/src/identity-and-account/identity-and-account.module.ts @@ -2,7 +2,7 @@ import { Module } from "@nestjs/common"; import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; import { EmployeesController } from "src/identity-and-account/employees/controllers/employees.controller"; import { EmployeesModule } from "src/identity-and-account/employees/employees.module"; -import { EmployeesService } from "src/identity-and-account/employees/services/employees.service"; +import { EmployeesGetService } from "src/identity-and-account/employees/services/employees-get.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"; @@ -12,6 +12,8 @@ import { AccessGetService } from "src/identity-and-account/user-module-access/se import { AccessUpdateService } from "src/identity-and-account/user-module-access/services/module-access-update.service"; import { UsersService } from "src/identity-and-account/users-management/services/users.service"; import { UsersModule } from "src/identity-and-account/users-management/users.module"; +import { EmployeesCreateService } from "src/identity-and-account/employees/services/employees-create.service"; +import { EmployeesUpdateService } from "src/identity-and-account/employees/services/employees-update.service"; @Module({ imports: [ @@ -26,7 +28,9 @@ import { UsersModule } from "src/identity-and-account/users-management/users.mod ModuleAccessController, ], providers: [ - EmployeesService, + EmployeesGetService, + EmployeesCreateService, + EmployeesUpdateService, PreferencesService, UsersService, EmailToIdResolver, diff --git a/src/time-and-attendance/leave-requests/dtos/leave-request.dto.ts b/src/time-and-attendance/leave-requests/dtos/leave-request.dto.ts index 9e0c822..14306ba 100644 --- a/src/time-and-attendance/leave-requests/dtos/leave-request.dto.ts +++ b/src/time-and-attendance/leave-requests/dtos/leave-request.dto.ts @@ -1,19 +1,17 @@ -import { IsEmail, IsArray, ArrayNotEmpty, ArrayUnique, IsISO8601, IsOptional, IsString, IsNumber, Min, Max, IsEnum } from "class-validator"; +import { IsEmail, IsArray, IsOptional, IsString, IsNumber, IsEnum, IsDateString } from "class-validator"; import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; import { Type } from "class-transformer"; //sets wich types to use -export const REQUEST_TYPES = Object.values(LeaveTypes) as readonly LeaveTypes[]; -export type RequestTypes = (typeof REQUEST_TYPES)[number]; +export const REQUEST_TYPES = Object.values(LeaveTypes) as readonly LeaveTypes[]; +export type RequestTypes = (typeof REQUEST_TYPES)[number]; export class LeaveRequestDto { - @IsEmail() + @IsEmail() @IsString() email!: string; @IsArray() - @ArrayNotEmpty() - @ArrayUnique() - @IsISO8601({}, { each: true }) + @IsDateString() dates!: string[]; @IsEnum(LeaveTypes) @@ -25,8 +23,6 @@ export class LeaveRequestDto { @IsOptional() @Type(() => Number) @IsNumber({ maxDecimalPlaces: 2 }) - @Min(0) - @Max(24) requested_hours?: number; @IsEnum(LeaveApprovalStatus) diff --git a/src/time-and-attendance/leave-requests/mappers/leave-requests.mapper.ts b/src/time-and-attendance/leave-requests/mappers/leave-requests.mapper.ts deleted file mode 100644 index 8999100..0000000 --- a/src/time-and-attendance/leave-requests/mappers/leave-requests.mapper.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; -import { Prisma } from "@prisma/client"; -import { leave_requests_select } from "src/time-and-attendance/utils/selects.utils"; - -type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leave_requests_select}>; - -const toNum = (value?: Prisma.Decimal | null) => - value !== null && value !== undefined ? Number(value) : undefined; - -// export function mapRowToView(row: LeaveRequestRow): LeaveRequestViewDto { -// const iso_date = row.dates?.toISOString().slice(0, 10); -// if (!iso_date) throw new Error(`Leave request #${row.id} has no date set.`); - -// return { -// id: row.id, -// leave_type: row.leave_type, -// date: iso_date, -// payable_hours: toNum(row.payable_hours), -// requested_hours: toNum(row.requested_hours), -// comment: row.comment, -// approval_status: row.approval_status, -// email: row.employee.user.email, -// employee_full_name: `${row.employee.user.first_name} ${row.employee.user.last_name}`, -// }; -// } \ No newline at end of file diff --git a/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts b/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts index 31e398a..40c38b9 100644 --- a/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts +++ b/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts @@ -58,7 +58,6 @@ export class PayPeriodsController { @Param('periodNumber', ParseIntPipe) period_no: number, @Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false, ): Promise> { - if (!email) throw new UnauthorizedException(`Session infos not found`); return this.queryService.getCrewOverview(year, period_no, email, include_subtree); } diff --git a/src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service.ts b/src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service.ts index a6b17c0..be7ad2e 100644 --- a/src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service.ts +++ b/src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service.ts @@ -10,12 +10,12 @@ import { Result } from "src/common/errors/result-error.factory"; @Injectable() export class SchedulePresetsApplyService { constructor( - private readonly prisma: PrismaService, + private readonly prisma: PrismaService, private readonly emailResolver: EmailToIdResolver ) { } async applyToTimesheet(email: string, id: number, start_date_iso: string): Promise> { - if (!DATE_ISO_FORMAT.test(start_date_iso)) return { success: false, error: 'start_date must be of format :YYYY-MM-DD' }; + if (!DATE_ISO_FORMAT.test(start_date_iso)) return { success: false, error: 'INVALID_PRESET' }; const employee_id = await this.emailResolver.findIdByEmail(email); if (!employee_id.success) return { success: false, error: employee_id.error } @@ -37,7 +37,7 @@ export class SchedulePresetsApplyService { }, }, }); - if (!preset) return { success: false, error: `Schedule preset with id: ${id} not found` }; + if (!preset) return { success: false, error: `PRESET_NOT_FOUND` }; const start_date = new Date(`${start_date_iso}T00:00:00.000Z`); @@ -91,7 +91,7 @@ export class SchedulePresetsApplyService { if (shift.end_time.getTime() <= shift.start_time.getTime()) { return { success: false, - error: `Invalid time range in preset day: ${week_day}, order: ${shift.sort_order}` + error: `INVALID_PRESET_SHIFT` }; } const conflict = existing.find((existe) => overlaps( @@ -101,7 +101,7 @@ export class SchedulePresetsApplyService { if (conflict) return { success: false, - error: `[SHIFT_OVERLAP] :Preset shift overlaps existing shift on ${start_date} + ${i}(week day ${week_day}) ` + error: `OVERLAPING_SHIFT` }; payload.push({ diff --git a/src/time-and-attendance/schedule-presets/services/schedule-presets-create.service.ts b/src/time-and-attendance/schedule-presets/services/schedule-presets-create.service.ts index 6207ba4..1daf617 100644 --- a/src/time-and-attendance/schedule-presets/services/schedule-presets-create.service.ts +++ b/src/time-and-attendance/schedule-presets/services/schedule-presets-create.service.ts @@ -23,103 +23,79 @@ export class SchedulePresetsCreateService { //validate email and fetch employee_id const employee_id = await this.emailResolver.findIdByEmail(email); if (!employee_id.success) return { success: false, error: employee_id.error }; - + //validate new unique name const existing = await this.prisma.schedulePresets.findFirst({ where: { name: dto.name, employee_id: employee_id.data }, select: { name: true }, }); if (!existing) return { success: false, error: 'INVALID_SCHEDULE_PRESET' }; - + const normalized_shifts = dto.preset_shifts.map((shift) => ({ - ...shift, - start: toDateFromHHmm(shift.start_time), - end: toDateFromHHmm(shift.end_time), - })); - - for (const preset_shifts of normalized_shifts) { - for (const other_shifts of normalized_shifts) { - //skip if same object or id week_day is not the same - if (preset_shifts === other_shifts) continue; - if (preset_shifts.week_day !== other_shifts.week_day) continue; - //check overlaping possibilities - const has_overlap = overlaps( - { start: preset_shifts.start, end: preset_shifts.end }, - { start: other_shifts.start, end: other_shifts.end }, - ) - if (has_overlap) return { success: false, error: 'SCHEDULE_PRESET_OVERLAP' }; + ...shift, + start: toDateFromHHmm(shift.start_time), + end: toDateFromHHmm(shift.end_time), + })); + + for (const preset_shifts of normalized_shifts) { + for (const other_shifts of normalized_shifts) { + //skip if same object or id week_day is not the same + if (preset_shifts === other_shifts) continue; + if (preset_shifts.week_day !== other_shifts.week_day) continue; + //check overlaping possibilities + const has_overlap = overlaps( + { start: preset_shifts.start, end: preset_shifts.end }, + { start: other_shifts.start, end: other_shifts.end }, + ) + if (has_overlap) return { success: false, error: 'SCHEDULE_PRESET_OVERLAP' }; + } } - } - - //validate bank_code_id/type and map them - const bank_code_results = await Promise.all(dto.preset_shifts.map((shift) => - this.typeResolver.findBankCodeIDByType(shift.type), - )); - for (const result of bank_code_results) { - if (!result.success) return { success: false, error: 'INVALID_SCHEDULE_PRESET' } - } - - await this.prisma.$transaction(async (tx) => { - //check if employee chose this preset has a default preset and ensure all others are false - if (dto.is_default) { - await tx.schedulePresets.updateMany({ - where: { employee_id: employee_id.data, is_default: true }, - data: { is_default: false }, - }); - } - - await tx.schedulePresets.create({ - data: { - employee_id: employee_id.data, - name: dto.name, - is_default: dto.is_default ?? false, - shifts: { - create: dto.preset_shifts.map((shift, index) => { - //validated bank_codes sent as a Result Array to access its data - const result = bank_code_results[index] as { success: true, data: number }; - return { - week_day: shift.week_day, - sort_order: shift.sort_order, - start_time: toDateFromHHmm(shift.start_time), - end_time: toDateFromHHmm(shift.end_time), - is_remote: shift.is_remote ?? false, - bank_code: { - //connect uses the FK links to set the bank_code_id - connect: { id: result.data }, - }, - } - }), + + //validate bank_code_id/type and map them + const bank_code_results = await Promise.all(dto.preset_shifts.map((shift) => + this.typeResolver.findBankCodeIDByType(shift.type), + )); + for (const result of bank_code_results) { + if (!result.success) return { success: false, error: 'INVALID_SCHEDULE_PRESET' } + } + + await this.prisma.$transaction(async (tx) => { + //check if employee chose this preset has a default preset and ensure all others are false + if (dto.is_default) { + await tx.schedulePresets.updateMany({ + where: { employee_id: employee_id.data, is_default: true }, + data: { is_default: false }, + }); + } + + await tx.schedulePresets.create({ + data: { + employee_id: employee_id.data, + name: dto.name, + is_default: dto.is_default ?? false, + shifts: { + create: dto.preset_shifts.map((shift, index) => { + //validated bank_codes sent as a Result Array to access its data + const result = bank_code_results[index] as { success: true, data: number }; + return { + week_day: shift.week_day, + sort_order: shift.sort_order, + start_time: toDateFromHHmm(shift.start_time), + end_time: toDateFromHHmm(shift.end_time), + is_remote: shift.is_remote ?? false, + bank_code: { + //connect uses the FK links to set the bank_code_id + connect: { id: result.data }, + }, + } + }), + }, }, - }, + }); }); - }); - return { success: true, data: true } + return { success: true, data: true } } catch (error) { - return { success: false, error: 'INVALID_SCHEDULE_PRESET'} + return { success: false, error: 'INVALID_SCHEDULE_PRESET' } } } - - - // //PRIVATE HELPERS - - //resolve bank_code_id using type and convert hours to TIME and valid shifts end/start - // private async normalizePresetShifts(preset_shift: SchedulePresetShiftsDto, schedul_preset: SchedulePresetsDto): Promise> { - - // const bank_code = await this.typeResolver.findIdAndModifierByType(preset_shift.type); - // if (!bank_code.success) return { success: false, error: 'INVALID_SCHEDULE_PRESET_SHIFT' }; - - // const start = await toDateFromHHmm(preset_shift.start_time); - // const end = await toDateFromHHmm(preset_shift.end_time); - - // //TODO: add a way to fetch - - - // const normalized_preset_shift:Normalized = { - // date: , - // start_time : start, - // end_time: end, - // bank_code_id: bank_code.data.id, - // } - // return { success: true data: normalized_preset_shift } - // } }