feat(employees): ajusted employees module, added an update method and route
This commit is contained in:
parent
0180fa3fdd
commit
fb0187c117
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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<Result<Partial<EmployeeDetailedDto>, 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<Result<Partial<EmployeeDetailedDto>, 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<Result<EmployeeDto[], string>> {
|
||||
return this.employeesService.findListEmployees();
|
||||
return this.getService.findListEmployees();
|
||||
}
|
||||
|
||||
@Post()
|
||||
@Post('create')
|
||||
@ModuleAccessAllowed(ModulesEnum.employee_management)
|
||||
async createEmployee(@Body() dto: EmployeeDetailedDto): Promise<Result<boolean, string>> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 { }
|
||||
|
|
|
|||
|
|
@ -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<Result<boolean, string>> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Result<boolean, string>> {
|
||||
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 = () => {
|
||||
|
||||
// }
|
||||
}
|
||||
|
|
@ -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<Result<boolean, string>> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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}`,
|
||||
// };
|
||||
// }
|
||||
|
|
@ -58,7 +58,6 @@ export class PayPeriodsController {
|
|||
@Param('periodNumber', ParseIntPipe) period_no: number,
|
||||
@Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false,
|
||||
): Promise<Result<PayPeriodOverviewDto, string>> {
|
||||
if (!email) throw new UnauthorizedException(`Session infos not found`);
|
||||
return this.queryService.getCrewOverview(year, period_no, email, include_subtree);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Result<ApplyResult, string>> {
|
||||
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({
|
||||
|
|
|
|||
|
|
@ -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<Result<Normalized, string>> {
|
||||
|
||||
// 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 }
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user