refactor(presets): refactor module to use Result Pattern commented to work on plugging error names
This commit is contained in:
parent
5bfeaaf4de
commit
3ceb2e0955
|
|
@ -90,7 +90,6 @@ export class ExpenseUpsertService {
|
||||||
});
|
});
|
||||||
if (!timesheet) return { success: false, error: `Timesheet ${dto.timesheet_id} not found` }
|
if (!timesheet) return { success: false, error: `Timesheet ${dto.timesheet_id} not found` }
|
||||||
|
|
||||||
|
|
||||||
//checks for modifications
|
//checks for modifications
|
||||||
const data: Prisma.ExpensesUpdateInput = {
|
const data: Prisma.ExpensesUpdateInput = {
|
||||||
...normed_expense.data,
|
...normed_expense.data,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { PayPeriodsQueryService } from "./services/pay-periods-query.service";
|
||||||
import { PayPeriodsController } from "./controllers/pay-periods.controller";
|
import { PayPeriodsController } from "./controllers/pay-periods.controller";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { PayPeriodsCommandService } from "src/time-and-attendance/pay-period/services/pay-periods-command.service";
|
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";
|
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { BadRequestException, ForbiddenException, Injectable, NotFoundException
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto";
|
import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto";
|
||||||
import { PayPeriodsQueryService } from "./pay-periods-query.service";
|
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()
|
@Injectable()
|
||||||
export class PayPeriodsCommandService {
|
export class PayPeriodsCommandService {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Injectable, BadRequestException, NotFoundException, ConflictException } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { Weekday, Prisma } from "@prisma/client";
|
import { Weekday, Prisma } from "@prisma/client";
|
||||||
import { DATE_ISO_FORMAT, WEEKDAY } from "src/common/utils/constants.utils";
|
import { DATE_ISO_FORMAT, WEEKDAY } from "src/common/utils/constants.utils";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
@ -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<Result<SchedulePresetsDto, string>> {
|
||||||
|
// 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<Result<SchedulePresetsDto, string>> {
|
||||||
|
// 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<Prisma.SchedulePresetShiftsCreateManyInput[], string> =
|
||||||
|
// 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<Result<number, string>> {
|
||||||
|
// 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<Result<Prisma.SchedulePresetShiftsCreateWithoutPresetInput[], string>> {
|
||||||
|
// 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<string, number>();
|
||||||
|
|
||||||
|
// 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<string>();
|
||||||
|
// 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};
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
@ -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 { 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 { 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 { 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 { TimesheetController } from "src/time-and-attendance/timesheets/controllers/timesheet.controller";
|
||||||
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";
|
||||||
import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service";
|
import { GetTimesheetsOverviewService } from "src/time-and-attendance/timesheets/services/timesheet-get-overview.service";
|
||||||
import { TimesheetsModule } from "src/time-and-attendance/time-tracker/timesheets/timesheets.module";
|
import { TimesheetsModule } from "src/time-and-attendance/timesheets/timesheets.module";
|
||||||
import { BankCodesResolver } from "src/common/mappers/bank-type-id.mapper";
|
import { BankCodesResolver } from "src/common/mappers/bank-type-id.mapper";
|
||||||
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
||||||
import { EmployeeTimesheetResolver } from "src/common/mappers/timesheet.mapper";
|
import { EmployeeTimesheetResolver } from "src/common/mappers/timesheet.mapper";
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<Result<SchedulePresetsDto, string>> {
|
|
||||||
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<Result<SchedulePresetsDto, string>> {
|
|
||||||
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<Prisma.SchedulePresetShiftsCreateManyInput[], string> =
|
|
||||||
// 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<Result<number, string>> {
|
|
||||||
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<Result<Prisma.SchedulePresetShiftsCreateWithoutPresetInput[], string>> {
|
|
||||||
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<string, number>();
|
|
||||||
|
|
||||||
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<string>();
|
|
||||||
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 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Req, UnauthorizedException } from "@nestjs/common";
|
import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Req, UnauthorizedException } from "@nestjs/common";
|
||||||
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
||||||
import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service";
|
import { GetTimesheetsOverviewService } from "src/time-and-attendance/timesheets/services/timesheet-get-overview.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";
|
||||||
import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes";
|
import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes";
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/common/utils/constants.utils
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
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 { Result } from "src/common/errors/result-error.factory";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toDateFromString, sevenDaysFrom, toStringFromDate, toHHmmFromDate } from "src/common/utils/date-utils";
|
import { toDateFromString, sevenDaysFrom, toStringFromDate, toHHmmFromDate } from "src/common/utils/date-utils";
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
|
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TimesheetController } from 'src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller';
|
import { TimesheetController } from 'src/time-and-attendance/timesheets/controllers/timesheet.controller';
|
||||||
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';
|
||||||
import { GetTimesheetsOverviewService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service';
|
import { GetTimesheetsOverviewService } from 'src/time-and-attendance/timesheets/services/timesheet-get-overview.service';
|
||||||
import { EmailToIdResolver } from 'src/common/mappers/email-id.mapper';
|
import { EmailToIdResolver } from 'src/common/mappers/email-id.mapper';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
Loading…
Reference in New Issue
Block a user