diff --git a/src/~misc_deprecated-files/after-hours.service.ts b/src/~misc_deprecated-files/after-hours.service.ts deleted file mode 100644 index c5e3425..0000000 --- a/src/~misc_deprecated-files/after-hours.service.ts +++ /dev/null @@ -1,79 +0,0 @@ -// import { BadRequestException, Injectable, Logger } from "@nestjs/common"; -// import { PrismaService } from "../prisma/prisma.service"; - - -// //THIS SERVICE IS NOT USED, RULES TO BE DETERMINED WITH MIKE/HR/ACCOUNTING -// @Injectable() -// export class AfterHoursService { -// private readonly logger = new Logger(AfterHoursService.name); -// private static readonly BUSINESS_START = 7; -// private static readonly BUSINESS_END = 18; -// private static readonly ROUND_MINUTES = 15; - -// constructor(private readonly prisma: PrismaService) {} - - -// private getPreBusinessMinutes(start: Date, end: Date): number { -// const biz_start = new Date(start); -// biz_start.setHours(AfterHoursService.BUSINESS_START, 0,0,0); - -// if (end>= start || start >= biz_start) { -// return 0; -// } - -// const segment_end = end < biz_start ? end : biz_start; -// const minutes = (segment_end.getTime() - start.getTime()) / 60000; - -// this.logger.debug(`getPreBusinessMintutes -> ${minutes.toFixed(1)}min`); -// return minutes; - -// } - -// private getPostBusinessMinutes(start: Date, end: Date): number { -// const biz_end = new Date(start); -// biz_end.setHours(AfterHoursService.BUSINESS_END,0,0,0); - -// if( end <= biz_end ) { -// return 0; -// } - -// const segment_start = start > biz_end ? start : biz_end; -// const minutes = (end.getTime() - segment_start.getTime()) / 60000; - -// this.logger.debug(`getPostBusinessMintutes -> ${minutes.toFixed(1)}min`); -// return minutes; - -// } - -// private roundToNearestQUarterMinute(minutes: number): number { -// const rounded = Math.round(minutes / AfterHoursService.ROUND_MINUTES) -// * AfterHoursService.ROUND_MINUTES; -// this.logger.debug(`roundToNearestQuarterMinute -> raw=${minutes.toFixed(1)}min, rounded= ${rounded}min`); -// return rounded; -// } - -// public computeAfterHours(start: Date, end:Date): number { -// if(end.getTime() <= start.getTime()) { -// throw new BadRequestException('The end cannot be before the starting of the shift'); -// } - -// if (start.toDateString() !== end.toDateString()) { -// throw new BadRequestException('you cannot enter a shift that start in a day and end in the next' + -// 'You must create 2 instances, one on the first day and the second during the next day.'); -// } - -// const pre_min = this.getPreBusinessMinutes(start, end); -// const post_min = this.getPostBusinessMinutes(start, end); -// const raw_aftermin = pre_min + post_min; - -// const rounded_min = this.roundToNearestQUarterMinute(raw_aftermin); - -// const hours = rounded_min / 60; -// const result = parseFloat(hours.toFixed(2)); - -// this.logger.debug(`computeAfterHours -> raw_aftermin = ${raw_aftermin.toFixed(1)}min, + -// rounded = ${rounded_min}min, hours = ${result.toFixed(2)}`); -// return result; -// } -// } - diff --git a/src/~misc_deprecated-files/create-expense.dto.ts b/src/~misc_deprecated-files/create-expense.dto.ts deleted file mode 100644 index 88ee933..0000000 --- a/src/~misc_deprecated-files/create-expense.dto.ts +++ /dev/null @@ -1,66 +0,0 @@ -// import { ApiProperty } from "@nestjs/swagger"; -// import { Type } from "class-transformer"; -// import { Allow, IsBoolean, IsDate, IsDateString, IsInt, IsNumber, IsOptional, IsString } from "class-validator"; - -// export class CreateExpenseDto { -// @ApiProperty({ -// example: 1, -// description: 'Unique ID of the expense (auto-generated)', -// }) -// @Allow() -// id?: number; - -// @ApiProperty({ -// example: 101, -// description: 'ID number for a set timesheet', -// }) -// @Type(()=> Number) -// @IsInt() -// timesheet_id: number; - -// @ApiProperty({ -// example: 7, -// description: 'ID number of an bank code (link with bank-codes)', -// }) -// @Type(() => Number) -// @IsInt() -// bank_code_id: number; - -// @ApiProperty({ -// example: '3018-10-20T00:00:00.000Z', -// description: 'Date where the expense was made', -// }) -// @IsDateString() -// date: string; - -// @ApiProperty({ -// example: 17.82, -// description: 'amount in $ for a refund', -// }) -// @Type(() => Number) -// @IsNumber() -// amount: number; - -// @ApiProperty({ -// example:'Spent for mileage between A and B', -// description:'explain`s why the expense was made' -// }) -// @IsString() -// comment: string; - -// @ApiProperty({ -// example: 'DENIED, APPROUVED, PENDING, etc...', -// description: 'validation status', -// }) -// @IsOptional() -// @IsBoolean() -// is_approved?: boolean; - -// @ApiProperty({ -// example:'Asked X to go there as an emergency response', -// description:'Supervisro`s justification for the spending of an employee' -// }) -// @IsString() -// @IsOptional() -// supervisor_comment?: string; -// } diff --git a/src/~misc_deprecated-files/create-timesheet.dto.ts b/src/~misc_deprecated-files/create-timesheet.dto.ts deleted file mode 100644 index ceb3807..0000000 --- a/src/~misc_deprecated-files/create-timesheet.dto.ts +++ /dev/null @@ -1,33 +0,0 @@ -// import { Type } from "class-transformer"; -// import { IsArray, IsOptional, IsString, Length, Matches, ValidateNested } from "class-validator"; - -// export class CreateTimesheetDto { - -// @IsString() -// @Matches(/^\d{4}-\d{2}-\d{2}$/) -// date!: string; - -// @IsString() -// @Length(1,64) -// type!: string; - -// @IsString() -// @Matches(/^\d{2}:\d{2}$/) -// start_time!: string; - -// @IsString() -// @Matches(/^\d{2}:\d{2}$/) -// end_time!: string; - -// @IsOptional() -// @IsString() -// @Length(0,512) -// comment?: string; -// } - -// export class CreateWeekShiftsDto { -// @IsArray() -// @ValidateNested({each:true}) -// @Type(()=> CreateTimesheetDto) -// shifts!: CreateTimesheetDto[]; -// } diff --git a/src/~misc_deprecated-files/date-time.constant.ts b/src/~misc_deprecated-files/date-time.constant.ts deleted file mode 100644 index 293a3d5..0000000 --- a/src/~misc_deprecated-files/date-time.constant.ts +++ /dev/null @@ -1,2 +0,0 @@ -// export const MS_PER_DAY = 86_400_000; -// export const MS_PER_HOUR = 3_600_000; \ No newline at end of file diff --git a/src/~misc_deprecated-files/expenses-command.service.ts b/src/~misc_deprecated-files/expenses-command.service.ts deleted file mode 100644 index 723bb7a..0000000 --- a/src/~misc_deprecated-files/expenses-command.service.ts +++ /dev/null @@ -1,249 +0,0 @@ -// import { BaseApprovalService } from "src/common/shared/base-approval.service"; -// import { Expenses, Prisma } from "@prisma/client"; -// import { PrismaService } from "src/prisma/prisma.service"; -// import { UpsertExpenseDto } from "../dtos/upsert-expense.dto"; -// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils"; -// import { ExpenseResponse, UpsertAction } from "../types and interfaces/expenses.types.interfaces"; -// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils"; -// import { EmployeeTimesheetResolver } from "src/modules/shared/utils/resolve-timesheet.utils"; -// import { -// BadRequestException, -// Injectable, -// NotFoundException -// } from "@nestjs/common"; -// import { -// assertAndTrimComment, -// computeAmountDecimal, -// computeMileageAmount, -// mapDbExpenseToDayResponse, -// normalizeType, -// parseAttachmentId -// } from "../utils/expenses.utils"; - -// @Injectable() -// export class ExpensesCommandService extends BaseApprovalService { -// constructor( -// prisma: PrismaService, -// private readonly bankCodesResolver: BankCodesResolver, -// private readonly timesheetsResolver: EmployeeTimesheetResolver, -// private readonly emailResolver: EmailToIdResolver, -// ) { super(prisma); } - -// //_____________________________________________________________________________________________ -// // APPROVAL TX-DELEGATE METHODS -// //_____________________________________________________________________________________________ - -// protected get delegate() { -// return this.prisma.expenses; -// } - -// protected delegateFor(transaction: Prisma.TransactionClient){ -// return transaction.expenses; -// } - -// async updateApproval(id: number, isApproved: boolean): Promise { -// return this.prisma.$transaction((transaction) => -// this.updateApprovalWithTransaction(transaction, id, isApproved), -// ); -// } - -// //_____________________________________________________________________________________________ -// // MASTER CRUD FUNCTION -// //_____________________________________________________________________________________________ -// readonly upsertExpensesByDate = async (email: string, date: string, dto: UpsertExpenseDto, -// ): Promise<{ action:UpsertAction; day: ExpenseResponse[] }> => { - -// //validates if there is an existing expense, at least 1 old or new -// const { old_expense, new_expense } = dto ?? {}; -// if(!old_expense && !new_expense) throw new BadRequestException('At least one expense must be provided'); - -// //validate date format -// const date_only = toDateOnly(date); -// if(Number.isNaN(date_only.getTime())) throw new BadRequestException('Invalid date format (expected: YYYY-MM-DD)'); - -// //resolve employee_id by email -// const employee_id = await this.emailResolver.findIdByEmail(email); - -// //make sure a timesheet existes -// const timesheet_id = await this.timesheetsResolver.findTimesheetIdByEmail(email, date_only); -// if(!timesheet_id) throw new NotFoundException(`no timesheet found for employee #${employee_id}`) -// const {id} = timesheet_id; - -// return this.prisma.$transaction(async (tx) => { -// const loadDay = async (): Promise => { -// const rows = await tx.expenses.findMany({ -// where: { -// timesheet_id: id, -// date: date_only, -// }, -// include: { -// bank_code: { -// select: { -// type: true, -// }, -// }, -// }, -// orderBy: [{ date: 'asc' }, { id: 'asc' }], -// }); - -// return rows.map((r) => -// mapDbExpenseToDayResponse({ -// date: r.date, -// amount: r.amount ?? 0, -// mileage: r.mileage ?? 0, -// comment: r.comment, -// is_approved: r.is_approved, -// bank_code: r.bank_code, -// })); -// }; - -// const normalizePayload = async (payload: { -// type: string; -// amount?: number; -// mileage?: number; -// comment: string; -// attachment?: string | number; -// }): Promise<{ -// type: string; -// bank_code_id: number; -// amount: Prisma.Decimal; -// mileage: number | null; -// comment: string; -// attachment: number | null; -// }> => { -// const type = normalizeType(payload.type); -// const comment = assertAndTrimComment(payload.comment); -// const attachment = parseAttachmentId(payload.attachment); - -// const { id: bank_code_id, modifier } = await this.bankCodesResolver.findByType(type); -// let amount = computeAmountDecimal(type, payload, modifier); -// let mileage: number | null = null; - -// if (type === 'MILEAGE') { -// mileage = Number(payload.mileage ?? 0); -// if (!(mileage > 0)) { -// throw new BadRequestException('Mileage required and must be > 0 for type MILEAGE'); -// } - -// const amountNumber = computeMileageAmount(mileage, modifier); -// amount = new Prisma.Decimal(amountNumber); - -// } else { -// if (!(typeof payload.amount === 'number' && payload.amount >= 0)) { -// throw new BadRequestException('Amount required for non-MILEAGE expense'); -// } -// amount = new Prisma.Decimal(payload.amount); -// } - -// if (attachment !== null) { -// const attachment_row = await tx.attachments.findUnique({ -// where: { id: attachment }, -// select: { status: true }, -// }); -// if (!attachment_row || attachment_row.status !== 'ACTIVE') { -// throw new BadRequestException('Attachment not found or inactive'); -// } -// } - -// return { -// type, -// bank_code_id, -// amount, -// mileage, -// comment, -// attachment -// }; -// }; - -// const findExactOld = async (norm: { -// bank_code_id: number; -// amount: Prisma.Decimal; -// mileage: number | null; -// comment: string; -// attachment: number | null; -// }) => { -// return tx.expenses.findFirst({ -// where: { -// timesheet_id: id, -// date: date_only, -// bank_code_id: norm.bank_code_id, -// amount: norm.amount, -// comment: norm.comment, -// attachment: norm.attachment, -// ...(norm.mileage !== null ? { mileage: norm.mileage } : { mileage: null }), -// }, -// select: { id: true }, -// }); -// }; - -// let action : UpsertAction; -// //_____________________________________________________________________________________________ -// // DELETE -// //_____________________________________________________________________________________________ -// if(old_expense && !new_expense) { -// const old_norm = await normalizePayload(old_expense); -// const existing = await findExactOld(old_norm); -// if(!existing) { -// throw new NotFoundException({ -// error_code: 'EXPENSE_STALE', -// message: 'The expense was modified or deleted by someone else', -// }); -// } -// await tx.expenses.delete({where: { id: existing.id } }); -// action = 'delete'; -// } -// //_____________________________________________________________________________________________ -// // CREATE -// //_____________________________________________________________________________________________ -// else if (!old_expense && new_expense) { -// const new_exp = await normalizePayload(new_expense); -// await tx.expenses.create({ -// data: { -// timesheet_id: id, -// date: date_only, -// bank_code_id: new_exp.bank_code_id, -// amount: new_exp.amount, -// mileage: new_exp.mileage, -// comment: new_exp.comment, -// attachment: new_exp.attachment, -// is_approved: false, -// }, -// }); -// action = 'create'; -// } -// //_____________________________________________________________________________________________ -// // UPDATE -// //_____________________________________________________________________________________________ -// else if(old_expense && new_expense) { -// const old_norm = await normalizePayload(old_expense); -// const existing = await findExactOld(old_norm); -// if(!existing) { -// throw new NotFoundException({ -// error_code: 'EXPENSE_STALE', -// message: 'The expense was modified or deleted by someone else', -// }); -// } - -// const new_exp = await normalizePayload(new_expense); -// await tx.expenses.update({ -// where: { id: existing.id }, -// data: { -// bank_code_id: new_exp.bank_code_id, -// amount: new_exp.amount, -// mileage: new_exp.mileage, -// comment: new_exp.comment, -// attachment: new_exp.attachment, -// }, -// }); -// action = 'update'; -// } -// else { -// throw new BadRequestException('Invalid upsert combination'); -// } - -// const day = await loadDay(); - -// return { action, day }; -// }); -// } -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/expenses-query.service.ts b/src/~misc_deprecated-files/expenses-query.service.ts deleted file mode 100644 index 6eafba2..0000000 --- a/src/~misc_deprecated-files/expenses-query.service.ts +++ /dev/null @@ -1,171 +0,0 @@ -// import { Injectable, NotFoundException } from "@nestjs/common"; -// import { PrismaService } from "src/prisma/prisma.service"; -// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils"; - -// @Injectable() -// export class ExpensesQueryService { -// constructor( -// private readonly prisma: PrismaService, -// private readonly employeeRepo: EmailToIdResolver, -// ) {} - - -// //fetchs all expenses for a selected employee using email, pay-period-year and number -// async findExpenseListByPayPeriodAndEmail( -// email: string, -// year: number, -// period_no: number -// ): Promise { -// //fetch employe_id using email -// const employee_id = await this.employeeRepo.findIdByEmail(email); -// if(!employee_id) throw new NotFoundException(`Employee with email: ${email} not found`); - -// //fetch pay-period using year and period_no -// const pay_period = await this.prisma.payPeriods.findFirst({ -// where: { -// pay_year: year, -// pay_period_no: period_no -// }, -// select: { period_start: true, period_end: true }, -// }); -// if(!pay_period) throw new NotFoundException(`Pay period ${year}- ${period_no} not found`); - -// const start = toUTCDateOnly(pay_period.period_start); -// const end = toUTCDateOnly(pay_period.period_end); - -// //sets rows data -// const rows = await this.prisma.expenses.findMany({ -// where: { -// date: { gte: start, lte: end }, -// timesheet: { is: { employee_id } }, -// }, -// orderBy: { date: 'asc'}, -// select: { -// amount: true, -// mileage: true, -// comment: true, -// is_approved: true, -// supervisor_comment: true, -// bank_code: {select: { type: true } }, -// }, -// }); - -// //declare return values -// const expenses: ExpenseDto[] = []; -// let total_amount = 0; -// let total_mileage = 0; - -// //set rows -// for(const row of rows) { -// const type = (row.bank_code?.type ?? '').toUpperCase(); -// const amount = round2(Number(row.amount ?? 0)); -// const mileage = round2(Number(row.mileage ?? 0)); - -// if(type === EXPENSE_TYPES.MILEAGE) { -// total_mileage += mileage; -// } else { -// total_amount += amount; -// } - -// //fills rows array -// expenses.push({ -// type, -// amount, -// mileage, -// comment: row.comment ?? '', -// is_approved: row.is_approved ?? false, -// supervisor_comment: row.supervisor_comment ?? '', -// }); -// } - -// return { -// expenses, -// total_expense: round2(total_amount), -// total_mileage: round2(total_mileage), -// }; -// } - -// //_____________________________________________________________________________________________ -// // Deprecated or unused methods -// //_____________________________________________________________________________________________ - -// // async create(dto: CreateExpenseDto): Promise { -// // const { timesheet_id, bank_code_id, date, amount:rawAmount, -// // comment, is_approved,supervisor_comment} = dto; -// // //fetches type and modifier -// // const bank_code = await this.prisma.bankCodes.findUnique({ -// // where: { id: bank_code_id }, -// // select: { type: true, modifier: true }, -// // }); -// // if(!bank_code) throw new NotFoundException(`bank_code #${bank_code_id} not found`); - -// // //if mileage -> service, otherwise the ratio is amount:1 -// // let final_amount: number; -// // if(bank_code.type === 'mileage') { -// // final_amount = await this.mileageService.calculateReimbursement(rawAmount, bank_code_id); -// // }else { -// // final_amount = parseFloat( (rawAmount * bank_code.modifier).toFixed(2)); -// // } - -// // return this.prisma.expenses.create({ -// // data: { -// // timesheet_id, -// // bank_code_id, -// // date, -// // amount: final_amount, -// // comment, -// // is_approved, -// // supervisor_comment -// // }, -// // include: { timesheet: { include: { employee: { include: { user: true }}}}, -// // bank_code: true, -// // }, -// // }) -// // } - -// // async findAll(filters: SearchExpensesDto): Promise { - // const where = buildPrismaWhere(filters); -// // const expenses = await this.prisma.expenses.findMany({ where }) -// // return expenses; -// // } - -// // async findOne(id: number): Promise { -// // const expense = await this.prisma.expenses.findUnique({ -// // where: { id }, -// // include: { timesheet: { include: { employee: { include: { user:true } } } }, -// // bank_code: true, -// // }, -// // }); -// // if (!expense) { -// // throw new NotFoundException(`Expense #${id} not found`); -// // } -// // return expense; -// // } - -// // async update(id: number, dto: UpdateExpenseDto): Promise { -// // await this.findOne(id); -// // const { timesheet_id, bank_code_id, date, amount, -// // comment, is_approved, supervisor_comment} = dto; -// // return this.prisma.expenses.update({ -// // where: { id }, -// // data: { -// // ...(timesheet_id !== undefined && { timesheet_id}), -// // ...(bank_code_id !== undefined && { bank_code_id }), -// // ...(date !== undefined && { date }), -// // ...(amount !== undefined && { amount }), -// // ...(comment !== undefined && { comment }), -// // ...(is_approved !== undefined && { is_approved }), -// // ...(supervisor_comment !== undefined && { supervisor_comment }), -// // }, -// // include: { timesheet: { include: { employee: { include: { user: true } } } }, -// // bank_code: true, -// // }, -// // }); -// // } - -// // async remove(id: number): Promise { -// // await this.findOne(id); -// // return this.prisma.expenses.delete({ where: { id } }); -// // } - -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/expenses.controller.ts b/src/~misc_deprecated-files/expenses.controller.ts deleted file mode 100644 index aea3161..0000000 --- a/src/~misc_deprecated-files/expenses.controller.ts +++ /dev/null @@ -1,95 +0,0 @@ -// import { Body, Controller, Get, Param, Put, } from "@nestjs/common"; -// import { Roles as RoleEnum } from '.prisma/client'; -// import { ApiBearerAuth, ApiTags } from "@nestjs/swagger"; -// import { RolesAllowed } from "src/common/decorators/roles.decorators"; -// import { ExpensesCommandService } from "../services/expenses-command.service"; -// import { UpsertExpenseDto } from "../dtos/upsert-expense.dto"; -// import { UpsertExpenseResult } from "../types and interfaces/expenses.types.interfaces"; -// import { DayExpensesDto } from "src/modules/timesheets/~misc_deprecated-files/timesheet-period.dto"; -// import { ExpensesQueryService } from "../services/expenses-query.service"; - -// @ApiTags('Expenses') -// @ApiBearerAuth('access-token') -// // @UseGuards() -// @Controller('Expenses') -// export class ExpensesController { -// constructor( -// private readonly query: ExpensesQueryService, -// private readonly command: ExpensesCommandService, -// ) {} - -// @Put('upsert/:email/:date') -// async upsert_by_date( -// @Param('email') email: string, -// @Param('date') date: string, -// @Body() dto: UpsertExpenseDto, -// ): Promise { -// return this.command.upsertExpensesByDate(email, date, dto); -// } - -// @Get('list/:email/:year/:period_no') -// async findExpenseListByPayPeriodAndEmail( -// @Param('email') email:string, -// @Param('year') year: number, -// @Param('period_no') period_no: number, -// ): Promise { -// return this.query.findExpenseListByPayPeriodAndEmail(email, year, period_no); -// } - -// //_____________________________________________________________________________________________ -// // Deprecated or unused methods -// //_____________________________________________________________________________________________ - -// // @Post() -// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) -// // @ApiOperation({ summary: 'Create expense' }) -// // @ApiResponse({ status: 201, description: 'Expense created',type: CreateExpenseDto }) -// // @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' }) -// // create(@Body() dto: CreateExpenseDto): Promise { -// // return this.query.create(dto); -// // } - -// // @Get() -// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) -// // @ApiOperation({ summary: 'Find all expenses' }) -// // @ApiResponse({ status: 201, description: 'List of expenses found',type: CreateExpenseDto, isArray: true }) -// // @ApiResponse({ status: 400, description: 'List of expenses not found' }) -// // @UsePipes(new ValidationPipe({ transform: true, whitelist: true })) -// // findAll(@Query() filters: SearchExpensesDto): Promise { -// // return this.query.findAll(filters); -// // } - -// // @Get(':id') -// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) -// // @ApiOperation({ summary: 'Find expense' }) -// // @ApiResponse({ status: 201, description: 'Expense found',type: CreateExpenseDto }) -// // @ApiResponse({ status: 400, description: 'Expense not found' }) -// // findOne(@Param('id', ParseIntPipe) id: number): Promise { -// // return this.query.findOne(id); -// // } - -// // @Patch(':id') -// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) -// // @ApiOperation({ summary: 'Expense shift' }) -// // @ApiResponse({ status: 201, description: 'Expense updated',type: CreateExpenseDto }) -// // @ApiResponse({ status: 400, description: 'Expense not found' }) -// // update(@Param('id', ParseIntPipe) id: number, @Body() dto: UpdateExpenseDto) { -// // return this.query.update(id,dto); -// // } - -// // @Delete(':id') -// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) -// // @ApiOperation({ summary: 'Delete expense' }) -// // @ApiResponse({ status: 201, description: 'Expense deleted',type: CreateExpenseDto }) -// // @ApiResponse({ status: 400, description: 'Expense not found' }) -// // remove(@Param('id', ParseIntPipe) id: number): Promise { -// // return this.query.remove(id); -// // } - -// // @Patch('approval/:id') -// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) -// // async approve(@Param('id', ParseIntPipe) id: number, @Body('is_approved', ParseBoolPipe) isApproved: boolean) { -// // return this.command.updateApproval(id, isApproved); -// // } - -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/expenses.types.interfaces.ts b/src/~misc_deprecated-files/expenses.types.interfaces.ts deleted file mode 100644 index 565d461..0000000 --- a/src/~misc_deprecated-files/expenses.types.interfaces.ts +++ /dev/null @@ -1,14 +0,0 @@ -// export type UpsertAction = 'create' | 'update' | 'delete'; - -// export interface ExpenseResponse { -// date: string; -// type: string; -// amount: number; -// comment: string; -// is_approved: boolean; -// }; - -// export type UpsertExpenseResult = { -// action: UpsertAction; -// day: ExpenseResponse[] -// }; \ No newline at end of file diff --git a/src/~misc_deprecated-files/expenses.utils.ts b/src/~misc_deprecated-files/expenses.utils.ts deleted file mode 100644 index c74aad4..0000000 --- a/src/~misc_deprecated-files/expenses.utils.ts +++ /dev/null @@ -1,111 +0,0 @@ -// import { BadRequestException } from "@nestjs/common"; -// import { ExpenseResponse } from "../types and interfaces/expenses.types.interfaces"; -// import { Prisma } from "@prisma/client"; - -// //uppercase and trim for validation -// export function normalizeType(type: string): string { -// return (type ?? '').trim().toUpperCase(); -// }; - -// //required comment after trim -// export function assertAndTrimComment(comment: string): string { -// const cmt = (comment ?? '').trim(); -// if(cmt.length === 0) { -// throw new BadRequestException('A comment is required'); -// } -// return cmt; -// }; - -// //rounding $ to 2 decimals -// export function roundMoney2(num: number): number { -// return Math.round((num + Number.EPSILON) * 100)/ 100; -// }; - -// export function computeMileageAmount(km: number, modifier: number): number { -// if(km < 0) throw new BadRequestException('mileage must be positive'); -// if(modifier < 0) throw new BadRequestException('modifier must be positive'); -// return roundMoney2(km * modifier); -// }; - -// //compat. types with Prisma.Decimal. work around Prisma import in utils. -// export type DecimalLike = -// | number -// | string -// | { toNumber?: () => number } -// | { toString?: () => string }; - - -// //safe conversion to number -// export function toNumberSafe(value: DecimalLike): number { -// if(typeof value === 'number') return value; -// if(value && typeof (value as any).toNumber === 'function') return (value as any).toNumber(); -// return Number( -// typeof (value as any)?.toString === 'function' -// ? (value as any).toString() -// : value, -// ); -// } - -// export const parseAttachmentId = (value: unknown): number | null => { -// if (value == null) { -// return null; -// } - -// if (typeof value === 'number') { -// if (!Number.isInteger(value) || value <= 0) { -// throw new BadRequestException('Invalid attachment id'); -// } -// return value; -// } - -// if (typeof value === 'string') { - -// const trimmed = value.trim(); -// if (!trimmed.length) return null; -// if (!/^\d+$/.test(trimmed)) throw new BadRequestException('Invalid attachment id'); - -// const parsed = Number(trimmed); -// if (parsed <= 0) throw new BadRequestException('Invalid attachment id'); - -// return parsed; -// } -// throw new BadRequestException('Invalid attachment id'); -// }; - - -// //map of a row for DayExpenseResponse -// export function mapDbExpenseToDayResponse(row: { -// date: Date; -// amount: Prisma.Decimal | number | string | null; -// mileage?: Prisma.Decimal | number | string | null; -// comment: string; -// is_approved: boolean; -// bank_code?: { type?: string | null } | null; -// }): ExpenseResponse { -// const yyyyMmDd = row.date.toISOString().slice(0,10); -// const toNum = (value: any)=> (value == null ? 0 : Number(value)); -// return { -// date: yyyyMmDd, -// type: normalizeType(row.bank_code?.type ?? 'UNKNOWN'), -// amount: toNum(row.amount), -// comment: row.comment, -// is_approved: row.is_approved, -// ...(row.mileage !== null ? { mileage: toNum(row.mileage) }: {}), -// }; -// } - -// export const computeAmountDecimal = ( -// type: string, -// payload: { -// amount?: number; -// mileage?: number; -// }, -// modifier: number, -// ): Prisma.Decimal => { -// if(type === 'MILEAGE') { -// const km = payload.mileage ?? 0; -// const amountNumber = computeMileageAmount(km, modifier); -// return new Prisma.Decimal(amountNumber); -// } -// return new Prisma.Decimal(payload.amount!); -// }; diff --git a/src/~misc_deprecated-files/get-shift-overview.dto.ts b/src/~misc_deprecated-files/get-shift-overview.dto.ts deleted file mode 100644 index 0921a48..0000000 --- a/src/~misc_deprecated-files/get-shift-overview.dto.ts +++ /dev/null @@ -1,10 +0,0 @@ -// import { Type } from "class-transformer"; -// import { IsInt, Min, Max } from "class-validator"; - -// export class GetShiftsOverviewDto { -// @Type(()=> Number) -// @IsInt() -// @Min(1) -// @Max(26) -// period_id: number; -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/leave-requests.select.ts b/src/~misc_deprecated-files/leave-requests.select.ts deleted file mode 100644 index 64453b4..0000000 --- a/src/~misc_deprecated-files/leave-requests.select.ts +++ /dev/null @@ -1,23 +0,0 @@ -// import { Prisma } from "@prisma/client"; - -// //custom prisma select to avoid employee_id exposure -// export const leaveRequestsSelect = { -// id: true, -// bank_code_id: true, -// leave_type: true, -// date: true, -// payable_hours: true, -// requested_hours: true, -// comment: true, -// approval_status: true, -// employee: { select: { -// id: true, -// user: { select: { -// email: true, -// first_name: true, -// last_name: true, -// }}, -// }}, -// } satisfies Prisma.LeaveRequestsSelect; - -// export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>; diff --git a/src/~misc_deprecated-files/regex.constant.ts b/src/~misc_deprecated-files/regex.constant.ts deleted file mode 100644 index 8b13789..0000000 --- a/src/~misc_deprecated-files/regex.constant.ts +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/~misc_deprecated-files/schedule-presets-apply.service.ts b/src/~misc_deprecated-files/schedule-presets-apply.service.ts deleted file mode 100644 index 9d78cd4..0000000 --- a/src/~misc_deprecated-files/schedule-presets-apply.service.ts +++ /dev/null @@ -1,126 +0,0 @@ -// import { Injectable } from "@nestjs/common"; -// import { Weekday, Prisma } from "@prisma/client"; -// import { DATE_ISO_FORMAT, WEEKDAY } from "src/common/utils/constants.utils"; -// import { PrismaService } from "src/prisma/prisma.service"; -// import { ApplyResult } from "src/time-and-attendance/utils/type.utils"; -// import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; -// import { Result } from "src/common/errors/result-error.factory"; - - -// @Injectable() -// export class SchedulePresetsApplyService { -// constructor( -// 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: 'INVALID_PRESET' }; - -// const employee_id = await this.emailResolver.findIdByEmail(email); -// if (!employee_id.success) return { success: false, error: employee_id.error } - -// const preset = await this.prisma.schedulePresets.findFirst({ -// where: { id }, -// include: { -// shifts: { -// orderBy: [{ week_day: 'asc' }, { sort_order: 'asc' }], -// select: { -// id: true, -// week_day: true, -// sort_order: true, -// start_time: true, -// end_time: true, -// is_remote: true, -// bank_code_id: true, -// }, -// }, -// }, -// }); -// if (!preset) return { success: false, error: `PRESET_NOT_FOUND` }; - - -// const start_date = new Date(`${start_date_iso}T00:00:00.000Z`); -// const timesheet = await this.prisma.timesheets.upsert({ -// where: { employee_id_start_date: { employee_id: employee_id.data, start_date: start_date } }, -// update: {}, -// create: { employee_id: employee_id.data, start_date: start_date }, -// select: { id: true }, -// }); - -// //index shifts by weekday -// const index_by_day = new Map(); -// for (const shift of preset.shifts) { -// const list = index_by_day.get(shift.week_day) ?? []; -// list.push(shift); -// index_by_day.set(shift.week_day, list); -// } - -// const addDays = (date: Date, days: number) => -// new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + days)); - -// const overlaps = (aStart: Date, aEnd: Date, bStart: Date, bEnd: Date) => -// aStart.getTime() < bEnd.getTime() && aEnd.getTime() > bStart.getTime(); - -// let created = 0; -// let skipped = 0; - -// await this.prisma.$transaction(async (tx) => { -// for (let i = 0; i < 7; i++) { -// const date = addDays(start_date, i); -// const week_day = WEEKDAY[date.getUTCDay()]; -// const shifts = index_by_day.get(week_day) ?? []; - -// if (shifts.length === 0) continue; - -// const existing = await tx.shifts.findMany({ -// where: { timesheet_id: timesheet.id, date: date }, -// orderBy: { start_time: 'asc' }, -// select: { -// start_time: true, -// end_time: true, -// bank_code_id: true, -// is_remote: true, -// comment: true, -// }, -// }); - -// const payload: Prisma.ShiftsCreateManyInput[] = []; - -// for (const shift of shifts) { -// if (shift.end_time.getTime() <= shift.start_time.getTime()) { -// return { -// success: false, -// error: `INVALID_PRESET_SHIFT` -// }; -// } -// const conflict = existing.find((existe) => overlaps( -// shift.start_time, shift.end_time, -// existe.start_time, existe.end_time, -// )); -// if (conflict) -// return { -// success: false, -// error: `OVERLAPING_SHIFT` -// }; - -// payload.push({ -// timesheet_id: timesheet.id, -// date: date, -// start_time: shift.start_time, -// end_time: shift.end_time, -// is_remote: shift.is_remote, -// comment: null, -// bank_code_id: shift.bank_code_id, -// }); -// } -// if (payload.length) { -// const response = await tx.shifts.createMany({ data: payload, skipDuplicates: true }); -// created += response.count; -// skipped += payload.length - response.count; -// } -// } -// }); -// return { success: true, data: { timesheet_id: timesheet.id, created, skipped } }; -// } -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/search-expense.dto.ts b/src/~misc_deprecated-files/search-expense.dto.ts deleted file mode 100644 index 665faac..0000000 --- a/src/~misc_deprecated-files/search-expense.dto.ts +++ /dev/null @@ -1,26 +0,0 @@ -// import { Type } from "class-transformer"; -// import { IsDateString, IsInt, IsOptional, IsString } from "class-validator"; - -// export class SearchExpensesDto { -// @IsOptional() -// @Type(()=> Number) -// @IsInt() -// timesheet_id?: number; - -// @IsOptional() -// @Type(()=> Number) -// @IsInt() -// bank_code_id?: number; - -// @IsOptional() -// @IsString() -// comment_contains?: string; - -// @IsOptional() -// @IsDateString() -// start_date: string; - -// @IsOptional() -// @IsDateString() -// end_date: string; -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/search-timesheet.dto.ts b/src/~misc_deprecated-files/search-timesheet.dto.ts deleted file mode 100644 index 6e1ba12..0000000 --- a/src/~misc_deprecated-files/search-timesheet.dto.ts +++ /dev/null @@ -1,20 +0,0 @@ -// import { Type } from "class-transformer"; -// import { IsBoolean, IsInt, IsOptional } from "class-validator"; - - -// export class SearchTimesheetDto { -// @IsOptional() -// @Type(() => Number) -// @IsInt() -// timesheet_id?: number; - -// @IsOptional() -// @Type(()=> Number) -// @IsInt() -// employee_id?: number; - -// @IsOptional() -// @Type(()=> Boolean) -// @IsBoolean() -// is_approved?: boolean; -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/shifts-command.service.ts b/src/~misc_deprecated-files/shifts-command.service.ts deleted file mode 100644 index 5544309..0000000 --- a/src/~misc_deprecated-files/shifts-command.service.ts +++ /dev/null @@ -1,194 +0,0 @@ -// import { BadRequestException, Injectable, Logger, NotFoundException } from "@nestjs/common"; -// import { DayShiftResponse } from "../types-and-interfaces/shifts-upsert.types"; -// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils"; -// import { Prisma, Shifts } from "@prisma/client"; -// import { UpsertShiftDto } from "../dtos/upsert-shift.dto"; -// import { BaseApprovalService } from "src/common/shared/base-approval.service"; -// import { PrismaService } from "src/prisma/prisma.service"; -// import { toDateOnly } from "../helpers/shifts-date-time-helpers"; -// import { UpsertAction } from "src/modules/shared/types/upsert-actions.types"; -// import { ShiftsHelpersService } from "../helpers/shifts.helpers"; -// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils"; - -// @Injectable() -// export class ShiftsCommandService extends BaseApprovalService { -// private readonly logger = new Logger(ShiftsCommandService.name); - -// constructor( -// prisma: PrismaService, -// private readonly emailResolver: EmailToIdResolver, -// private readonly typeResolver: BankCodesResolver, -// private readonly helpersService: ShiftsHelpersService, -// ) { super(prisma); } - -// //_____________________________________________________________________________________________ -// // APPROVAL AND DELEGATE METHODS -// //_____________________________________________________________________________________________ -// protected get delegate() { -// return this.prisma.shifts; -// } - -// protected delegateFor(transaction: Prisma.TransactionClient) { -// return transaction.shifts; -// } - -// async updateApproval(id: number, is_approved: boolean): Promise { -// return this.prisma.$transaction((transaction) => -// this.updateApprovalWithTransaction(transaction, id, is_approved), -// ); -// } - - -// //TODO: modifier le Master Crud pour recevoir l'ensemble des shifts de la pay-period et trier sur l'action 'create'| 'update' | 'delete' -// //_____________________________________________________________________________________________ -// // MASTER CRUD METHOD -// //_____________________________________________________________________________________________ -// async upsertShifts( -// email: string, -// action: UpsertAction, -// dto: UpsertShiftDto, -// ): Promise<{ -// action: UpsertAction; -// day: DayShiftResponse[]; -// }> { -// if (!dto.old_shift && !dto.new_shift) throw new BadRequestException('At least one of old or new shift must be provided'); - -// const date = dto.new_shift?.date ?? dto.old_shift?.date; -// if (!date) throw new BadRequestException("A date (YYYY-MM-DD) must be provided in old_shift or new_shift"); -// if (dto.old_shift?.date && dto.new_shift?.date && dto.old_shift.date !== dto.new_shift.date) { -// throw new BadRequestException('old_shift.date and new_shift.date must be identical'); -// } - -// const employee_id = await this.emailResolver.findIdByEmail(email);//resolve employee id using email - -// if(action === 'create') { -// if(!dto.new_shift || dto.old_shift) { -// throw new BadRequestException(`Only new_shift must be provided for create`); -// } -// return this.createShift(employee_id, date, dto); -// } -// if(action === 'update'){ -// if(!dto.old_shift || !dto.new_shift) { -// throw new BadRequestException(`Both new_shift and old_shift must be provided for update`); -// } -// return this.updateShift(employee_id, date, dto); -// } -// throw new BadRequestException(`Unknown action: ${action}`); -// } - -// //_________________________________________________________________ -// // CREATE -// //_________________________________________________________________ -// private async createShift( -// employee_id: number, -// date_iso: string, -// dto: UpsertShiftDto, -// ): Promise<{action: UpsertAction; day: DayShiftResponse[]}> { -// return this.prisma.$transaction(async (tx) => { -// const date_only = toDateOnly(date_iso); -// const timesheet = await this.helpersService.ensureTimesheet(tx, employee_id, date_only); -// if(!timesheet) throw new NotFoundException('Timesheet not found') -// const new_norm_shift = await this.helpersService.normalizeRequired(dto.new_shift); -// const new_bank_code_id = await this.helpersService.resolveBankIdRequired(tx, new_norm_shift.type, 'new_shift'); - -// const day_shifts = await this.helpersService.getDayShifts(tx, timesheet.id, date_only); - -// await this.helpersService.assertNoOverlap(day_shifts, new_norm_shift); - -// await tx.shifts.create({ -// data: { -// timesheet_id: timesheet.id, -// date: date_only, -// start_time: new_norm_shift.start_time, -// end_time: new_norm_shift.end_time, -// is_remote: new_norm_shift.is_remote, -// is_approved: new_norm_shift.is_approved, -// comment: new_norm_shift.comment ?? null, -// bank_code_id: new_bank_code_id, -// }, -// }); -// await this.helpersService.afterWriteOvertimeAndLog(tx, employee_id, date_only); -// const fresh_shift = await this.helpersService.getDayShifts(tx, timesheet.id, date_only); -// return { action: 'create', day: await this.helpersService.mapDay(fresh_shift)}; -// }); -// } - -// //_________________________________________________________________ -// // UPDATE -// //_________________________________________________________________ -// private async updateShift( -// employee_id: number, -// date_iso: string, -// dto: UpsertShiftDto, -// ): Promise<{ action: UpsertAction; day: DayShiftResponse[];}>{ -// return this.prisma.$transaction(async (tx) => { -// const date_only = toDateOnly(date_iso); -// const timesheet = await this.helpersService.ensureTimesheet(tx, employee_id, date_only); -// if(!timesheet) throw new NotFoundException('Timesheet not found') - -// const old_norm_shift = await this.helpersService.normalizeRequired(dto.old_shift, 'old_shift'); -// const new_norm_shift = await this.helpersService.normalizeRequired(dto.new_shift, 'new_shift'); - -// const old_bank_code = await this.typeResolver.findByType(old_norm_shift.type); -// const new_bank_code = await this.typeResolver.findByType(new_norm_shift.type); - -// const day_shifts = await this.helpersService.getDayShifts(tx, timesheet.id, date_only); -// const existing = await this.helpersService.findExactOldShift(tx, { -// timesheet_id: timesheet.id, -// date_only, -// norm: old_norm_shift, -// bank_code_id: old_bank_code.id, -// }); -// if(!existing) throw new NotFoundException('[SHIFT_STALE]- The shift was modified or deleted by someone else'); - -// await this.helpersService.assertNoOverlap(day_shifts, new_norm_shift, existing.id); - -// await tx.shifts.update({ -// where: { id: existing.id }, -// data: { -// start_time: new_norm_shift.start_time, -// end_time: new_norm_shift.end_time, -// is_remote: new_norm_shift.is_remote, -// comment: new_norm_shift.comment ?? null, -// bank_code_id: new_bank_code.id, -// }, -// }); -// await this.helpersService.afterWriteOvertimeAndLog(tx, employee_id, date_only); -// const fresh_shift = await this.helpersService.getDayShifts(tx, timesheet.id, date_only); -// return { action: 'update', day: await this.helpersService.mapDay(fresh_shift)}; -// }); - -// } - -// //_________________________________________________________________ -// // DELETE -// //_________________________________________________________________ -// async deleteShift( -// email: string, -// date_iso: string, -// dto: UpsertShiftDto, -// ){ -// return this.prisma.$transaction(async (tx) => { -// const date_only = toDateOnly(date_iso); //converts to Date format -// const employee_id = await this.emailResolver.findIdByEmail(email); - -// const timesheet = await this.helpersService.ensureTimesheet(tx, employee_id, date_only); -// if(!timesheet) throw new NotFoundException('Timesheet not found') -// const norm_shift = await this.helpersService.normalizeRequired(dto.old_shift, 'old_shift'); -// const bank_code_id = await this.typeResolver.findByType(norm_shift.type); - -// const existing = await this.helpersService.findExactOldShift(tx, { -// timesheet_id: timesheet.id, -// date_only, -// norm: norm_shift, -// bank_code_id: bank_code_id.id, -// }); -// if(!existing) throw new NotFoundException('[SHIFT_STALE]- The shift was modified or deleted by someone else'); - -// await tx.shifts.delete({ where: { id: existing.id } }); - -// await this.helpersService.afterWriteOvertimeAndLog(tx, employee_id, date_only); -// }); -// } -// } - diff --git a/src/~misc_deprecated-files/shifts-overview-row.interface.ts b/src/~misc_deprecated-files/shifts-overview-row.interface.ts deleted file mode 100644 index b82576e..0000000 --- a/src/~misc_deprecated-files/shifts-overview-row.interface.ts +++ /dev/null @@ -1,10 +0,0 @@ -// export interface OverviewRow { -// full_name: string; -// supervisor: string; -// total_regular_hrs: number; -// total_evening_hrs: number; -// total_overtime_hrs: number; -// total_expenses: number; -// total_mileage: number; -// is_approved: boolean; -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/shifts-query.service.ts b/src/~misc_deprecated-files/shifts-query.service.ts deleted file mode 100644 index be978f0..0000000 --- a/src/~misc_deprecated-files/shifts-query.service.ts +++ /dev/null @@ -1,114 +0,0 @@ -// import { Injectable, NotFoundException } from "@nestjs/common"; -// import { PrismaService } from "src/prisma/prisma.service"; -// import { NotificationsService } from "src/modules/notifications/services/notifications.service"; -// import { computeHours } from "src/common/utils/date-utils"; -// import { OverviewRow } from "../types-and-interfaces/shifts-overview-row.interface"; - -// // const DAILY_LIMIT_HOURS = Number(process.env.DAILY_LIMIT_HOURS ?? 12); - -// @Injectable() -// export class ShiftsQueryService { -// constructor( -// private readonly prisma: PrismaService, -// private readonly notifs: NotificationsService, -// ) {} - -// async getSummary(period_id: number): Promise { -// //fetch pay-period to display -// const period = await this.prisma.payPeriods.findFirst({ -// where: { pay_period_no: period_id }, -// }); -// if(!period) { -// throw new NotFoundException(`pay-period ${period_id} not found`); -// } -// const { period_start, period_end } = period; - -// //prepare shifts and expenses for display -// const shifts = await this.prisma.shifts.findMany({ -// where: { date: { gte: period_start, lte: period_end } }, -// include: { -// bank_code: true, -// timesheet: { include: { -// employee: { include: { -// user:true, -// supervisor: { include: { user: true } }, -// } }, -// } }, -// }, -// }); - -// const expenses = await this.prisma.expenses.findMany({ -// where: { date: { gte: period_start, lte: period_end } }, -// include: { -// bank_code: true, -// timesheet: { include: { employee: { -// include: { user:true, -// supervisor: { include: { user:true } }, -// } }, -// } }, -// }, -// }); - -// const mapRow = new Map(); - -// for(const shift of shifts) { -// const employeeId = shift.timesheet.employee.user_id; -// const user = shift.timesheet.employee.user; -// const sup = shift.timesheet.employee.supervisor?.user; - -// let row = mapRow.get(employeeId); -// if(!row) { -// row = { -// full_name: `${user.first_name} ${user.last_name}`, -// supervisor: sup? `${sup.first_name} ${sup.last_name }` : '', -// total_regular_hrs: 0, -// total_evening_hrs: 0, -// total_overtime_hrs: 0, -// total_expenses: 0, -// total_mileage: 0, -// is_approved: false, -// }; -// } -// const hours = computeHours(shift.start_time, shift.end_time); - -// switch(shift.bank_code.type) { -// case 'regular' : row.total_regular_hrs += hours; -// break; -// case 'evening' : row.total_evening_hrs += hours; -// break; -// case 'overtime' : row.total_overtime_hrs += hours; -// break; -// default: row.total_regular_hrs += hours; -// } -// mapRow.set(employeeId, row); -// } - -// for(const exp of expenses) { -// const employee_id = exp.timesheet.employee.user_id; -// const user = exp.timesheet.employee.user; -// const sup = exp.timesheet.employee.supervisor?.user; - -// let row = mapRow.get(employee_id); -// if(!row) { -// row = { -// full_name: `${user.first_name} ${user.last_name}`, -// supervisor: sup? `${sup.first_name} ${sup.last_name }` : '', -// total_regular_hrs: 0, -// total_evening_hrs: 0, -// total_overtime_hrs: 0, -// total_expenses: 0, -// total_mileage: 0, -// is_approved: false, -// }; -// } -// const amount = Number(exp.amount); -// row.total_expenses += amount; -// if(exp.bank_code.type === 'mileage') { -// row.total_mileage += amount; -// } -// mapRow.set(employee_id, row); -// } -// //return by default the list of employee in ascending alphabetical order -// return Array.from(mapRow.values()).sort((a,b) => a.full_name.localeCompare(b.full_name)); -// } -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/shifts-upsert.types.ts b/src/~misc_deprecated-files/shifts-upsert.types.ts deleted file mode 100644 index ea21afe..0000000 --- a/src/~misc_deprecated-files/shifts-upsert.types.ts +++ /dev/null @@ -1,17 +0,0 @@ -// export type DayShiftResponse = { -// start_time: string; -// end_time: string; -// type: string; -// is_remote: boolean; -// comment: string | null; -// } - -// export type ShiftPayload = { -// date: string; -// start_time: string; -// end_time: string; -// type: string; -// is_remote: boolean; -// is_approved: boolean; -// comment?: string | null; -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/shifts.controller.ts b/src/~misc_deprecated-files/shifts.controller.ts deleted file mode 100644 index 88a1637..0000000 --- a/src/~misc_deprecated-files/shifts.controller.ts +++ /dev/null @@ -1,87 +0,0 @@ -// import { Body, Controller, Delete, Get, Header, Param, ParseBoolPipe, ParseIntPipe, Patch, Put, Query, } from "@nestjs/common"; -// import { RolesAllowed } from "src/common/decorators/roles.decorators"; -// import { Roles as RoleEnum } from '.prisma/client'; -// import { ApiBearerAuth, ApiTags } from "@nestjs/swagger"; -// import { ShiftsCommandService } from "../services/shifts-command.service"; -// import { ShiftsQueryService } from "../services/shifts-query.service"; -// import { GetShiftsOverviewDto } from "../dtos/get-shift-overview.dto"; -// import { ShiftPayloadDto, UpsertShiftDto } from "../dtos/upsert-shift.dto"; -// import { OverviewRow } from "../types-and-interfaces/shifts-overview-row.interface"; -// import { UpsertAction } from "src/modules/shared/types/upsert-actions.types"; - -// @ApiTags('Shifts') -// @ApiBearerAuth('access-token') -// // @UseGuards() -// @Controller('shifts') -// export class ShiftsController { -// constructor( -// private readonly shiftsService: ShiftsQueryService, -// private readonly shiftsCommandService: ShiftsCommandService, -// ){} - -// @Put('upsert/:email') -// async upsert_by_date( -// @Param('email') email_param: string, -// @Query('action') action: UpsertAction, -// @Body() payload: UpsertShiftDto, -// ) { -// return this.shiftsCommandService.upsertShifts(email_param, action, payload); -// } - -// @Delete('delete/:email/:date') -// async remove( -// @Param('email') email: string, -// @Param('date') date: string, -// @Body() payload: UpsertShiftDto, -// ) { -// return this.shiftsCommandService.deleteShift(email, date, payload); -// } - -// @Patch('approval/:id') -// //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) -// async approve(@Param('id', ParseIntPipe) id: number, @Body('is_approved', ParseBoolPipe) isApproved: boolean) { -// return this.shiftsCommandService.updateApproval(id, isApproved); -// } - -// @Get('summary') -// async getSummary( @Query() query: GetShiftsOverviewDto): Promise { -// return this.shiftsService.getSummary(query.period_id); -// } - -// @Get('export.csv') -// @Header('Content-Type', 'text/csv; charset=utf-8') -// @Header('Content-Disposition', 'attachment; filename="shifts-validation.csv"') -// async exportCsv(@Query() query: GetShiftsOverviewDto): Promise{ -// const rows = await this.shiftsService.getSummary(query.period_id); -// //CSV Headers -// const header = [ -// 'full_name', -// 'supervisor', -// 'total_regular_hrs', -// 'total_evening_hrs', -// 'total_overtime_hrs', -// 'total_expenses', -// 'total_mileage', -// 'is_validated' -// ].join(',') + '\n'; - -// //CSV rows -// const body = rows.map(r => { -// const esc = (str: string) => `"${str.replace(/"/g, '""')}"`; - -// return [ -// esc(r.full_name), -// esc(r.supervisor), -// r.total_regular_hrs.toFixed(2), -// r.total_evening_hrs.toFixed(2), -// r.total_overtime_hrs.toFixed(2), -// r.total_expenses.toFixed(2), -// r.total_mileage.toFixed(2), -// r.is_approved, -// ].join(','); -// }).join('\n'); - -// return Buffer.from('\uFEFF' + header + body, 'utf8'); -// } - -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/shifts.helpers.ts b/src/~misc_deprecated-files/shifts.helpers.ts deleted file mode 100644 index a0e1e1c..0000000 --- a/src/~misc_deprecated-files/shifts.helpers.ts +++ /dev/null @@ -1,103 +0,0 @@ -// import { BadRequestException, UnprocessableEntityException, NotFoundException, ConflictException } from "@nestjs/common"; -// import { Prisma, Shifts } from "@prisma/client"; -// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils"; -// import { OvertimeService } from "src/modules/business-logics/services/overtime.service"; - -// export type Tx = Prisma.TransactionClient; -// export type Normalized = Awaited>; - -// export class ShiftsHelpersService { - -// constructor( -// private readonly bankTypeResolver: BankCodesResolver, -// private readonly overtimeService: OvertimeService, -// ) { } - -// async ensureTimesheet(tx: Tx, employee_id: number, date_only: Date) { -// const start_of_week = weekStartSunday(date_only); -// return tx.timesheets.findUnique({ -// where: { employee_id_start_date: { employee_id, start_date: start_of_week } }, -// select: { id: true }, -// }); -// } - -// async normalizeRequired( -// raw: UpsertShiftDto['new_shift'] | UpsertShiftDto['old_shift'] | undefined | null, -// label: 'old_shift' | 'new_shift' = 'new_shift', -// ): Promise { -// if (!raw) throw new BadRequestException(`${label} is required`); -// const norm = await normalizeShiftPayload(raw); -// if (norm.end_time.getTime() <= norm.start_time.getTime()) { -// throw new UnprocessableEntityException(` ${label}.end_time must be > ${label}.start_time`); -// } -// return norm; -// } - -// async resolveBankIdRequired(tx: Tx, type: string, label: 'old_shift' | 'new_shift'): Promise { -// const found = await this.bankTypeResolver.findByType(type, tx); -// const id = found?.id; -// if (typeof id !== 'number') { -// throw new NotFoundException(`bank code not found for ${label}.type: ${type ?? ''}`); -// } -// return id; -// } - -// async getDayShifts(tx: Tx, timesheet_id: number, date_only: Date) { -// return tx.shifts.findMany({ -// where: { timesheet_id, date: date_only }, -// include: { bank_code: true }, -// orderBy: { start_time: 'asc' }, -// }); -// } - -// async findExactOldShift( -// tx: Tx, -// params: { -// timesheet_id: number; -// date_only: Date; -// norm: Normalized; -// bank_code_id: number; -// comment?: string; -// }, -// ) { -// const { timesheet_id, date_only, norm, bank_code_id } = params; -// return tx.shifts.findFirst({ -// where: { -// timesheet_id, -// date: date_only, -// start_time: norm.start_time, -// end_time: norm.end_time, -// is_remote: norm.is_remote, -// is_approved: norm.is_approved, -// comment: norm.comment ?? null, -// bank_code_id, -// }, -// select: { id: true }, -// }); -// } - -// async afterWriteOvertimeAndLog(tx: Tx, employee_id: number, date_only: Date) { -// // Switch regular → weekly overtime si > 40h -// await this.overtimeService.transformRegularHoursToWeeklyOvertime(employee_id, date_only, tx); -// const daily = await this.overtimeService.getDailyOvertimeHours(employee_id, date_only); -// const weekly = await this.overtimeService.getWeeklyOvertimeHours(employee_id, date_only); -// // const [daily, weekly] = await Promise.all([ -// // this.overtimeService.getDailyOvertimeHoursForDay(employee_id, date_only), -// // this.overtimeService.getWeeklyOvertimeHours(employee_id, date_only), -// // ]); -// return { daily, weekly }; -// } - -// async mapDay( -// fresh: Array, -// ): Promise { -// return fresh.map((s) => ({ -// start_time: toStringFromHHmm(s.start_time), -// end_time: toStringFromHHmm(s.end_time), -// type: s.bank_code?.type ?? 'UNKNOWN', -// is_remote: s.is_remote, -// comment: s.comment ?? null, -// })); -// } -// } - diff --git a/src/~misc_deprecated-files/shifts.utils.ts b/src/~misc_deprecated-files/shifts.utils.ts deleted file mode 100644 index bfa5402..0000000 --- a/src/~misc_deprecated-files/shifts.utils.ts +++ /dev/null @@ -1,58 +0,0 @@ -// import { NotFoundException } from "@nestjs/common"; - -// export function overlaps( -// a_start_ms: number, -// a_end_ms: number, -// b_start_ms: number, -// b_end_ms: number, -// ): boolean { -// return a_start_ms < b_end_ms && b_start_ms < a_end_ms; -// } - -// export function resolveBankCodeByType(type: string): Promise { -// const bank = this.prisma.bankCodes.findFirst({ -// where: { type }, -// select: { id: true }, -// }); -// if (!bank) { -// throw new NotFoundException({ error_code: 'SHIFT_TYPE_UNKNOWN', message: `unknown shift type: ${type}` }); -// } -// return bank.id; -// } - -// export function normalizeShiftPayload(payload: { -// date: string, -// start_time: string, -// end_time: string, -// type: string, -// is_remote: boolean, -// is_approved: boolean, -// comment?: string | null, -// }) { -// //normalize shift's infos -// const date = payload.date?.trim(); -// const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date ?? ''); -// if (!m) throw new Error(`Invalid date format (expected YYYY-MM-DD): "${payload.date}"`); -// const year = Number(m[1]), mo = Number(m[2]), d = Number(m[3]); - -// const asLocalDateOn = (input: string): Date => { -// // HH:mm ? -// const hm = /^(\d{2}):(\d{2})$/.exec((input ?? '').trim()); -// if (hm) return new Date(year, mo - 1, d, Number(hm[1]), Number(hm[2]), 0, 0); -// const iso = new Date(input); -// if (isNaN(iso.getTime())) throw new Error(`Invalid time: "${input}"`); -// return new Date(year, mo - 1, d, iso.getHours(), iso.getMinutes(), iso.getSeconds(), iso.getMilliseconds()); -// }; - -// const start_time = asLocalDateOn(payload.start_time); -// const end_time = asLocalDateOn(payload.end_time); - -// const type = (payload.type || '').trim().toUpperCase(); -// const is_remote = payload.is_remote; -// const is_approved = payload.is_approved; -// //normalize comment -// const trimmed = typeof payload.comment === 'string' ? payload.comment.trim() : null; -// const comment = trimmed && trimmed.length > 0 ? trimmed : null; - -// return { date, start_time, end_time, type, is_remote, is_approved, comment }; -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/timesheet-period.dto.ts b/src/~misc_deprecated-files/timesheet-period.dto.ts deleted file mode 100644 index 004026e..0000000 --- a/src/~misc_deprecated-files/timesheet-period.dto.ts +++ /dev/null @@ -1,75 +0,0 @@ -// export class TimesheetDto { -// start_day: string; -// end_day: string; -// label: string; -// shifts: ShiftDto[]; -// expenses: ExpenseDto[] -// is_approved: boolean; -// } - -// export class ShiftDto { -// date: string; -// type: string; -// start_time: string; -// end_time : string; -// comment: string; -// is_approved: boolean; -// is_remote: boolean; -// } - -// export class ExpenseDto { -// type: string; -// amount: number; -// mileage: number; -// comment: string; -// is_approved: boolean; -// supervisor_comment: string; -// } - -// export type DayShiftsDto = ShiftDto[]; - -// export class DetailedShifts { -// shifts: DayShiftsDto; -// regular_hours: number; -// evening_hours: number; -// overtime_hours: number; -// emergency_hours: number; -// comment: string; -// short_date: string; -// break_durations?: number; -// } - -// export class DayExpensesDto { -// expenses: ExpenseDto[] = []; -// total_mileage: number; -// total_expense: number; -// } - -// export class WeekDto { -// is_approved: boolean; -// shifts: { -// sun: DetailedShifts; -// mon: DetailedShifts; -// tue: DetailedShifts; -// wed: DetailedShifts; -// thu: DetailedShifts; -// fri: DetailedShifts; -// sat: DetailedShifts; -// } -// expenses: { -// sun: DayExpensesDto; -// mon: DayExpensesDto; -// tue: DayExpensesDto; -// wed: DayExpensesDto; -// thu: DayExpensesDto; -// fri: DayExpensesDto; -// sat: DayExpensesDto; -// } -// } - -// export class TimesheetPeriodDto { -// weeks: WeekDto[]; -// employee_full_name: string; -// } - - diff --git a/src/~misc_deprecated-files/timesheet.helpers.ts b/src/~misc_deprecated-files/timesheet.helpers.ts deleted file mode 100644 index 930a277..0000000 --- a/src/~misc_deprecated-files/timesheet.helpers.ts +++ /dev/null @@ -1,67 +0,0 @@ -// import { MS_PER_DAY } from "src/modules/shared/constants/date-time.constant"; -// import { DAY_KEYS, DayKey } from "./timesheet.types"; - -// export function toUTCDateOnly(date: Date | string): Date { -// const d = new Date(date); -// return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())); -// } - -// export function addDays(date:Date, days: number): Date { -// return new Date(date.getTime() + days * MS_PER_DAY); -// } - -// export function endOfDayUTC(date: Date | string): Date { -// const d = toUTCDateOnly(date); -// return new Date(d.getTime() + MS_PER_DAY - 1); -// } - -// export function isBetweenUTC(date: Date, start: Date, end_inclusive: Date): boolean { -// const time = date.getTime(); -// return time >= start.getTime() && time <= end_inclusive.getTime(); -// } - -// export function toTimeString(date: Date): string { -// const hours = String(date.getUTCHours()).padStart(2,'0'); -// const minutes = String(date.getUTCMinutes()).padStart(2,'0'); -// return `${hours}:${minutes}`; -// } - -// export function round2(num: number) { -// return Math.round(num * 100) / 100; -// } - -// export function shortDate(date:Date): string { -// const mm = String(date.getUTCMonth()+1).padStart(2,'0'); -// const dd = String(date.getUTCDate()).padStart(2,'0'); -// return `${mm}/${dd}`; -// } - -// export function dayKeyFromDate(date: Date, useUTC = true): DayKey { -// const index = useUTC ? date.getUTCDay() : date.getDay(); // 0=Sunday..6=Saturday -// return DAY_KEYS[index]; -// } - -// export const toHHmm = (date: Date) => date.toISOString().slice(11, 16); - -// export function parseISODate(iso: string): Date { -// const [ y, m, d ] = iso.split('-').map(Number); -// return new Date(y, (m ?? 1) - 1, d ?? 1); -// } - -// export function parseHHmm(t: string): Date { -// const [ hh, mm ] = t.split(':').map(Number); -// return new Date(1970, 0, 1, hh || 0, mm || 0, 0, 0); -// } - -// export const toNum = (value: any) => -// value && typeof value.toNumber === 'function' ? value.toNumber() : -// typeof value === 'number' ? value : -// value ? Number(value) : 0; - - -// export const upper = (s?: string | null) => String(s ?? '').toUpperCase(); - -// export const toRangeFromPeriod = (period: { period_start: Date; period_end: Date }) => ({ -// from: toUTCDateOnly(period.period_start), -// to: endOfDayUTC(period.period_end), -// }); \ No newline at end of file diff --git a/src/~misc_deprecated-files/timesheet.mappers.ts b/src/~misc_deprecated-files/timesheet.mappers.ts deleted file mode 100644 index 64a21f8..0000000 --- a/src/~misc_deprecated-files/timesheet.mappers.ts +++ /dev/null @@ -1,111 +0,0 @@ -// import { DayExpensesDto, WeekDto, DetailedShifts, TimesheetPeriodDto } from "../dtos/timesheet-period.dto"; -// import { ShiftRow, ExpenseRow, ExpensesAmount, TimesheetMap } from "./timesheet.types"; -// import { addDays, shortDate, toNum, upper } from "./timesheet.helpers"; -// import { Prisma } from "@prisma/client"; - - -// //mappers -// export const mapShiftRow = (shift: { -// date: Date; -// start_time: Date; -// end_time: Date; -// comment?: string | null; -// is_approved: boolean; -// is_remote: boolean; -// bank_code: { type: string }; -// }): ShiftRow => ({ -// date: shift.date, -// start_time: shift.start_time, -// end_time: shift.end_time, -// comment: shift.comment ?? '', -// is_approved: shift.is_approved, -// is_remote: shift.is_remote, -// type: upper(shift.bank_code.type), -// }); - -// export const mapExpenseRow = (expense: { -// date: Date; -// amount: Prisma.Decimal | number | null; -// mileage: Prisma.Decimal | number | null; -// comment?: string | null; -// is_approved: boolean; -// supervisor_comment?: string|null; -// bank_code: { type: string }, -// }): ExpenseRow => ({ -// date: expense.date, -// amount: toNum(expense.amount), -// mileage: toNum(expense.mileage), -// comment: expense.comment ?? '', -// is_approved: expense.is_approved, -// supervisor_comment: expense.supervisor_comment ?? '', -// type: upper(expense.bank_code.type), -// }); - -// // Factories -// export function makeEmptyDayExpenses(): DayExpensesDto { -// return { -// expenses: [], -// total_expense: -1, -// total_mileage: -1, -// }; -// } - -// export function makeEmptyWeek(week_start: Date): WeekDto { -// const make_empty_shifts = (offset: number): DetailedShifts => ({ -// shifts: [], -// regular_hours: 0, -// evening_hours: 0, -// emergency_hours: 0, -// overtime_hours: 0, -// comment: '', -// short_date: shortDate(addDays(week_start, offset)), -// break_durations: 0, -// }); -// return { -// is_approved: true, -// shifts: { -// sun: make_empty_shifts(0), -// mon: make_empty_shifts(1), -// tue: make_empty_shifts(2), -// wed: make_empty_shifts(3), -// thu: make_empty_shifts(4), -// fri: make_empty_shifts(5), -// sat: make_empty_shifts(6), -// }, -// expenses: { -// sun: makeEmptyDayExpenses(), -// mon: makeEmptyDayExpenses(), -// tue: makeEmptyDayExpenses(), -// wed: makeEmptyDayExpenses(), -// thu: makeEmptyDayExpenses(), -// fri: makeEmptyDayExpenses(), -// sat: makeEmptyDayExpenses(), -// }, -// }; -// } - -// export function makeEmptyPeriod(): TimesheetPeriodDto { -// return { weeks: [makeEmptyWeek(new Date()), makeEmptyWeek(new Date())], employee_full_name: '' }; -// } - -// export const makeAmounts = (): ExpensesAmount => ({ -// expense: 0, -// mileage: 0, -// }); - -// export function makeEmptyTimesheet(params: { -// start_day: string; -// end_day: string; -// label: string; -// is_approved?: boolean; -// }): TimesheetMap { -// const { start_day, end_day, label, is_approved = false } = params; -// return { -// start_day, -// end_day, -// label, -// shifts: [], -// expenses: [], -// is_approved, -// }; -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/timesheet.selectors.ts b/src/~misc_deprecated-files/timesheet.selectors.ts deleted file mode 100644 index 698f5a7..0000000 --- a/src/~misc_deprecated-files/timesheet.selectors.ts +++ /dev/null @@ -1,46 +0,0 @@ -// import { EXPENSE_ASC_ORDER, EXPENSE_SELECT } from "../../../shared/selects/expenses.select"; -// import { Injectable, NotFoundException } from "@nestjs/common"; -// import { SHIFT_ASC_ORDER, SHIFT_SELECT } from "../../../shared/selects/shifts.select"; -// import { PAY_PERIOD_SELECT } from "../../../shared/selects/pay-periods.select"; -// import { PrismaService } from "src/prisma/prisma.service"; - -// @Injectable() -// export class TimesheetSelectorsService { -// constructor(readonly prisma: PrismaService){} - -// async getPayPeriod(pay_year: number, pay_period_no: number) { -// const period = await this.prisma.payPeriods.findFirst({ -// where: { pay_year, pay_period_no }, -// select: PAY_PERIOD_SELECT , -// }); -// if(!period) throw new NotFoundException(`period ${pay_year}-${pay_period_no} not found`); -// return period; -// } - -// async getShifts(employee_id: number, from: Date, to: Date) { -// return this.prisma.shifts.findMany({ -// where: {timesheet: { is: { employee_id } }, date: { gte: from, lte: to } }, -// select: SHIFT_SELECT, -// orderBy: SHIFT_ASC_ORDER, -// }); -// } - -// async getExpenses(employee_id: number, from: Date, to: Date) { -// return this.prisma.expenses.findMany({ -// where: { timesheet: {is: { employee_id } }, date: { gte: from, lte: to } }, -// select: EXPENSE_SELECT, -// orderBy: EXPENSE_ASC_ORDER, -// }); -// } - -// async getTimesheetWithShiftsAndExpenses(employee_id: number, start_date_week: Date) { -// return this.prisma.timesheets.findUnique({ -// where: { employee_id_start_date: { employee_id, start_date: start_date_week } }, -// select: { -// is_approved: true, -// shift: { select: SHIFT_SELECT, orderBy: SHIFT_ASC_ORDER }, -// expense: { select: EXPENSE_SELECT, orderBy: EXPENSE_ASC_ORDER }, -// }, -// }); -// } -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/timesheet.types.ts b/src/~misc_deprecated-files/timesheet.types.ts deleted file mode 100644 index 1d1d0be..0000000 --- a/src/~misc_deprecated-files/timesheet.types.ts +++ /dev/null @@ -1,74 +0,0 @@ -// export type ShiftRow = { -// date: Date; -// start_time: Date; -// end_time: Date; -// comment: string; -// is_approved?: boolean; -// is_remote: boolean; -// type: string -// }; -// export type ExpenseRow = { -// date: Date; -// amount: number; -// mileage?: number | null; -// comment: string; -// type: string; -// is_approved?: boolean; -// supervisor_comment: string; -// }; - -// export type TimesheetMap = { -// start_day: string; -// end_day: string; -// label: string; -// shifts: ShiftRow[]; -// expenses: ExpenseRow[] -// is_approved: boolean; -// } - -// // Types -// export const SHIFT_TYPES = { -// REGULAR: 'REGULAR', -// EVENING: 'EVENING', -// OVERTIME: 'OVERTIME', -// EMERGENCY: 'EMERGENCY', -// HOLIDAY: 'HOLIDAY', -// VACATION: 'VACATION', -// SICK: 'SICK', -// } as const; - -// export const EXPENSE_TYPES = { -// MILEAGE: 'MILEAGE', -// EXPENSE: 'EXPENSES', -// PER_DIEM: 'PER_DIEM', -// ON_CALL: 'ON_CALL', -// } as const; - -// //makes the strings indexes for arrays -// export const DAY_KEYS = ['sun','mon','tue','wed','thu','fri','sat'] as const; -// export type DayKey = typeof DAY_KEYS[number]; - -// //shifts's hour by type -// export type ShiftsHours = { -// regular: number; -// evening: number; -// overtime: number; -// emergency: number; -// sick: number; -// vacation: number; -// holiday: number; -// }; -// export const make_hours = (): ShiftsHours => ({ -// regular: 0, -// evening: 0, -// overtime: 0, -// emergency: 0, -// sick: 0, -// vacation: 0, -// holiday: 0, -// }); - -// export type ExpensesAmount = { -// expense: number; -// mileage: number; -// }; \ No newline at end of file diff --git a/src/~misc_deprecated-files/timesheet.utils.ts b/src/~misc_deprecated-files/timesheet.utils.ts deleted file mode 100644 index b1ca369..0000000 --- a/src/~misc_deprecated-files/timesheet.utils.ts +++ /dev/null @@ -1,171 +0,0 @@ -// import { -// DayKey, DAY_KEYS, EXPENSE_TYPES, ExpenseRow, -// SHIFT_TYPES, ShiftRow, make_hours, ShiftsHours, ExpensesAmount -// } from "./timesheet.types"; -// import { -// isBetweenUTC, dayKeyFromDate, toTimeString, round2, -// toUTCDateOnly, endOfDayUTC, addDays -// } from "./timesheet.helpers"; -// import { WeekDto, ShiftDto, TimesheetPeriodDto, DayExpensesDto, ExpenseDto } from "../dtos/timesheet-period.dto"; -// import { getWeekStart, getWeekEnd, formatDateISO } from "src/common/utils/date-utils"; -// import { makeAmounts, makeEmptyWeek } from "./timesheet.mappers"; -// import { toDateString } from "src/modules/pay-periods/utils/pay-year.util"; -// import { MS_PER_HOUR } from "src/modules/shared/constants/date-time.constant"; - -// export function computeWeekRange(week_offset = 0){ -// //sets current week Sunday -> Saturday -// const base = new Date(); -// const offset = new Date(base); -// offset.setDate(offset.getDate() + (week_offset * 7)); - -// const start = getWeekStart(offset, 0); -// const end = getWeekEnd(start); -// const start_day = formatDateISO(start); -// const end_day = formatDateISO(end); -// const label = `${(start_day)}.${(end_day)}`; - -// return { start, end, start_day, end_day, label } -// }; - -// export function buildWeek( -// week_start: Date, -// week_end: Date, -// shifts: ShiftRow[], -// expenses: ExpenseRow[], -// ): WeekDto { -// const week = makeEmptyWeek(week_start); -// let all_approved = true; - -// const day_times: Record> = DAY_KEYS.reduce((acc, key) => { -// acc[key] = []; return acc; -// }, {} as Record>); - -// const day_hours: Record = DAY_KEYS.reduce((acc, key) => { -// acc[key] = make_hours(); return acc; -// }, {} as Record); - -// const day_amounts: Record = DAY_KEYS.reduce((acc, key) => { -// acc[key] = makeAmounts(); return acc; -// }, {} as Record); - -// const day_expense_rows: Record = DAY_KEYS.reduce((acc, key) => { -// acc[key] = { -// expenses: [{ -// type: '', -// amount: -1, -// mileage: -1, -// comment: '', -// is_approved: false, -// supervisor_comment: '', -// }], -// total_expense: -1, -// total_mileage: -1, -// }; -// return acc; -// }, {} as Record); - -// //regroup hours per type of shifts -// const week_shifts = shifts.filter(shift => isBetweenUTC(shift.date, week_start, week_end)); -// for (const shift of week_shifts) { -// const key = dayKeyFromDate(shift.date, true); -// week.shifts[key].shifts.push({ -// date: toDateString(shift.date), -// type: shift.type, -// start_time: toTimeString(shift.start_time), -// end_time: toTimeString(shift.end_time), -// comment: shift.comment, -// is_approved: shift.is_approved ?? true, -// is_remote: shift.is_remote, -// } as ShiftDto); - -// day_times[key].push({ start: shift.start_time, end: shift.end_time}); - -// const duration = Math.max(0, (shift.end_time.getTime() - shift.start_time.getTime())/ MS_PER_HOUR); -// const type = (shift.type || '').toUpperCase(); - -// if ( type === SHIFT_TYPES.REGULAR) day_hours[key].regular += duration; -// else if( type === SHIFT_TYPES.EVENING) day_hours[key].evening += duration; -// else if( type === SHIFT_TYPES.EMERGENCY) day_hours[key].emergency += duration; -// else if( type === SHIFT_TYPES.OVERTIME) day_hours[key].overtime += duration; -// else if( type === SHIFT_TYPES.SICK) day_hours[key].sick += duration; -// else if( type === SHIFT_TYPES.VACATION) day_hours[key].vacation += duration; -// else if( type === SHIFT_TYPES.HOLIDAY) day_hours[key].holiday += duration; - -// all_approved = all_approved && (shift.is_approved ?? true ); -// } - -// //regroupe amounts to type of expenses -// const week_expenses = expenses.filter(expense => isBetweenUTC(expense.date, week_start, week_end)); -// for (const expense of week_expenses) { -// const key = dayKeyFromDate(expense.date, true); -// const type = (expense.type || '').toUpperCase(); - -// const row: ExpenseDto = { -// type, -// amount: round2(expense.amount ?? 0), -// mileage: round2(expense.mileage ?? 0), -// comment: expense.comment ?? '', -// is_approved: expense.is_approved ?? true, -// supervisor_comment: expense.supervisor_comment ?? '', -// }; - -// day_expense_rows[key].expenses.push(row); - -// if(type === EXPENSE_TYPES.MILEAGE) { -// day_amounts[key].mileage += row.mileage ?? 0; -// } else { -// day_amounts[key].expense += row.amount; -// } - -// all_approved = all_approved && row.is_approved; -// } - -// for (const key of DAY_KEYS) { -// //return exposed dto data -// week.shifts[key].regular_hours = round2(day_hours[key].regular); -// week.shifts[key].evening_hours = round2(day_hours[key].evening); -// week.shifts[key].overtime_hours = round2(day_hours[key].overtime); -// week.shifts[key].emergency_hours = round2(day_hours[key].emergency); - -// //calculate gaps between shifts -// const times = day_times[key].sort((a,b) => a.start.getTime() - b.start.getTime()); -// let gaps = 0; -// for (let i = 1; i < times.length; i++) { -// const gap = (times[i].start.getTime() - times[i - 1].end.getTime()) / MS_PER_HOUR; -// if(gap > 0) gaps += gap; -// } -// week.shifts[key].break_durations = round2(gaps); - -// //daily totals -// const totals = day_amounts[key]; - -// day_expense_rows[key].total_mileage = round2(totals.mileage); -// day_expense_rows[key].total_expense = round2(totals.expense); -// } - -// week.is_approved = all_approved; -// return week; -// } - -// export function buildPeriod( -// period_start: Date, -// period_end: Date, -// shifts: ShiftRow[], -// expenses: ExpenseRow[], -// employeeFullName = '' -// ): TimesheetPeriodDto { -// const week1_start = toUTCDateOnly(period_start); -// const week1_end = endOfDayUTC(addDays(week1_start, 6)); -// const week2_start = toUTCDateOnly(addDays(week1_start, 7)); -// const week2_end = endOfDayUTC(period_end); - -// const weeks: WeekDto[] = [ -// buildWeek(week1_start, week1_end, shifts, expenses), -// buildWeek(week2_start, week2_end, shifts, expenses), -// ]; - -// return { -// weeks, -// employee_full_name: employeeFullName, -// }; -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/timesheets-command.service.ts b/src/~misc_deprecated-files/timesheets-command.service.ts deleted file mode 100644 index 9fb3b52..0000000 --- a/src/~misc_deprecated-files/timesheets-command.service.ts +++ /dev/null @@ -1,137 +0,0 @@ -// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; -// import { EmployeeTimesheetResolver } from "src/modules/shared/utils/resolve-timesheet.utils"; -// import { getWeekEnd, getWeekStart } from "src/common/utils/date-utils"; -// import { parseISODate, parseHHmm } from "./utils-helpers-others/timesheet.helpers"; -// import { TimesheetsQueryService } from "./timesheets-query.service"; -// import { BaseApprovalService } from "src/common/shared/base-approval.service"; -// import { Prisma, Timesheets } from "@prisma/client"; -// import { CreateTimesheetDto } from "./create-timesheet.dto"; -// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils"; -// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils"; -// import { PrismaService } from "src/prisma/prisma.service"; -// import { TimesheetMap } from "./utils-helpers-others/timesheet.types"; -// import { Shift, Expense } from "../dtos/timesheet.dto"; - -// @Injectable() -// export class TimesheetsCommandService extends BaseApprovalService{ -// constructor( -// prisma: PrismaService, -// private readonly query: TimesheetsQueryService, -// private readonly emailResolver: EmailToIdResolver, -// private readonly timesheetResolver: EmployeeTimesheetResolver, -// private readonly bankTypeResolver: BankCodesResolver, -// ) {super(prisma);} -// //_____________________________________________________________________________________________ -// // APPROVAL AND DELEGATE METHODS -// //_____________________________________________________________________________________________ -// protected get delegate() { -// return this.prisma.timesheets; -// } - -// protected delegateFor(transaction: Prisma.TransactionClient) { -// return transaction.timesheets; -// } - -// async updateApproval(id: number, isApproved: boolean): Promise { -// return this.prisma.$transaction((transaction) => -// this.updateApprovalWithTransaction(transaction, id, isApproved), -// ); -// } - -// async cascadeApprovalWithtx(transaction: Prisma.TransactionClient, timesheetId: number, isApproved: boolean): Promise { -// const timesheet = await this.updateApprovalWithTransaction(transaction, timesheetId, isApproved); -// await transaction.shifts.updateMany({ -// where: { timesheet_id: timesheetId }, -// data: { is_approved: isApproved }, -// }); -// await transaction.expenses.updateManyAndReturn({ -// where: { timesheet_id: timesheetId }, -// data: { is_approved: isApproved }, -// }); -// return timesheet; -// } - -// /**_____________________________________________________________________________________________ -// create/update/delete shifts and expenses from 1 or many timesheet(s) - -// -this function receives an email and an array of timesheets - -// -this function will find the timesheets with all shifts and expenses -// -this function will calculate total hours, total expenses, filtered by types, -// cumulate in daily and weekly. - -// -the timesheet_id will be determined using the employee email -// -with the timesheet_id, all shifts and expenses will be fetched - -// -with shift_id and expense_id, this function will compare both -// datas from the DB and from the body of the function and then: -// -it will create a shift if no shift is found in the DB -// -it will update a shift if a shift is found in the DB -// -it will delete a shift if a shift is found and no data is received from the frontend - -// This function will be used for the Timesheet Page for an employee to enter, modify or delete and entry -// This function will also be used in the modal of the timesheet validation page to -// allow a supervisor to enter, modify or delete and entry of a selected employee -// _____________________________________________________________________________________________*/ - -// async findTimesheetsByEmailAndPayPeriod(email: string, year: number, period_no: number, timesheets: Timesheets[]): Promise { -// const employee_id = await this.emailResolver.findIdByEmail(email); - - -// return timesheets; -// } - -// async upsertOrDeleteShiftsByEmailAndDate(email:string, shift_ids: Shift[]) {} - -// async upsertOrDeleteExpensesByEmailAndDate(email:string, expenses_id: Expense[]) {} - - - - - -// //_____________________________________________________________________________________________ -// // -// //_____________________________________________________________________________________________ - -// async createWeekShiftsAndReturnOverview( -// email:string, -// shifts: CreateTimesheetDto[], -// week_offset = 0, -// ): Promise { -// //fetchs employee matchint user's email -// const employee_id = await this.emailResolver.findIdByEmail(email); -// if(!employee_id) throw new NotFoundException(`employee for ${ email } not found`); - -// //insure that the week starts on sunday and finishes on saturday -// const base = new Date(); -// base.setDate(base.getDate() + week_offset * 7); -// const start_week = getWeekStart(base, 0); -// const end_week = getWeekEnd(start_week); - -// const timesheet = await this.timesheetResolver.findTimesheetIdByEmail(email, base) -// if(!timesheet) throw new NotFoundException(`no timesheet found for employe ${employee_id}`); - -// //validations and insertions -// for(const shift of shifts) { -// const date = parseISODate(shift.date); -// if (date < start_week || date > end_week) throw new BadRequestException(`date ${shift.date} not in current week`); - -// const bank_code = await this.bankTypeResolver.findByType(shift.type) -// if(!bank_code) throw new BadRequestException(`Invalid bank_code type: ${shift.type}`); - -// await this.prisma.shifts.create({ -// data: { -// timesheet_id: timesheet.id, -// bank_code_id: bank_code.id, -// date: date, -// start_time: parseHHmm(shift.start_time), -// end_time: parseHHmm(shift.end_time), -// comment: shift.comment ?? null, -// is_approved: false, -// is_remote: false, -// }, -// }); -// } -// return this.query.getTimesheetByEmail(email, week_offset); -// } -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/timesheets-query.service.ts b/src/~misc_deprecated-files/timesheets-query.service.ts deleted file mode 100644 index 7999702..0000000 --- a/src/~misc_deprecated-files/timesheets-query.service.ts +++ /dev/null @@ -1,54 +0,0 @@ -// import { makeEmptyTimesheet, mapExpenseRow, mapShiftRow } from './utils-helpers-others/timesheet.mappers'; -// import { buildPeriod, computeWeekRange } from './utils-helpers-others/timesheet.utils'; -// import { TimesheetSelectorsService } from './utils-helpers-others/timesheet.selectors'; -// import { TimesheetPeriodDto } from './timesheet-period.dto'; -// import { toRangeFromPeriod } from './utils-helpers-others/timesheet.helpers'; -// import { EmailToIdResolver } from 'src/modules/shared/utils/resolve-email-id.utils'; -// import { FullNameResolver } from 'src/modules/shared/utils/resolve-full-name.utils'; -// import { PrismaService } from 'src/prisma/prisma.service'; -// import { TimesheetMap } from './utils-helpers-others/timesheet.types'; -// import { Injectable } from '@nestjs/common'; - - -// @Injectable() -// export class TimesheetsQueryService { -// constructor( -// private readonly prisma: PrismaService, -// private readonly emailResolver: EmailToIdResolver, -// private readonly fullNameResolver: FullNameResolver, -// private readonly selectors: TimesheetSelectorsService, -// ) {} - -// async findAll(year: number, period_no: number, email: string): Promise { -// const employee_id = await this.emailResolver.findIdByEmail(email); //finds the employee using email -// const full_name = await this.fullNameResolver.resolveFullName(employee_id); //finds the employee full name using employee_id -// const period = await this.selectors.getPayPeriod(year, period_no);//finds the pay period using year and period_no -// const{ from, to } = toRangeFromPeriod(period); //finds start and end dates -// //finds all shifts from selected period -// const [raw_shifts, raw_expenses] = await Promise.all([ -// this.selectors.getShifts(employee_id, from, to), -// this.selectors.getExpenses(employee_id, from, to), -// ]); -// // data mapping -// const shifts = raw_shifts.map(mapShiftRow); -// const expenses = raw_expenses.map(mapExpenseRow); - -// return buildPeriod(period.period_start, period.period_end, shifts , expenses, full_name); -// } - - - -// async getTimesheetByEmail(email: string, week_offset = 0): Promise { -// const employee_id = await this.emailResolver.findIdByEmail(email); //finds the employee using email -// const { start, start_day, end_day, label } = computeWeekRange(week_offset); -// const timesheet = await this.selectors.getTimesheetWithShiftsAndExpenses(employee_id, start); //fetch timesheet shifts and expenses -// if(!timesheet) return makeEmptyTimesheet({ start_day, end_day, label}); - -// //maps all shifts of selected timesheet -// const shifts = timesheet.shift.map(mapShiftRow); -// const expenses = timesheet.expense.map(mapExpenseRow); - - -// return { start_day, end_day, label, shifts, expenses, is_approved: timesheet.is_approved}; -// } -// } diff --git a/src/~misc_deprecated-files/timesheets.controller.ts b/src/~misc_deprecated-files/timesheets.controller.ts deleted file mode 100644 index 8ba1dee..0000000 --- a/src/~misc_deprecated-files/timesheets.controller.ts +++ /dev/null @@ -1,51 +0,0 @@ -// import { BadRequestException, Body, Controller, Get, Param, ParseIntPipe, Post, Query } from '@nestjs/common'; -// import { TimesheetsQueryService } from './timesheets-query.service'; -// import { CreateWeekShiftsDto } from './create-timesheet.dto'; -// import { RolesAllowed } from "src/common/decorators/roles.decorators"; -// import { Roles as RoleEnum } from '.prisma/client'; -// import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; -// import { TimesheetsCommandService } from './timesheets-command.service'; -// import { TimesheetPeriodDto } from './timesheet-period.dto'; -// import { TimesheetMap } from './timesheet.types'; - - -// @ApiTags('Timesheets') -// @ApiBearerAuth('access-token') -// // @UseGuards() -// @Controller('timesheets') -// export class TimesheetsController { -// constructor( -// private readonly timesheetsQuery: TimesheetsQueryService, -// private readonly timesheetsCommand: TimesheetsCommandService, -// ) {} - -// @Get() -// //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) -// async getPeriodByQuery( -// @Query('year', ParseIntPipe ) year: number, -// @Query('period_no', ParseIntPipe ) period_no: number, -// @Query('email') email?: string -// ): Promise { -// if(!email || !(email = email.trim())) throw new BadRequestException('Query param "email" is mandatory for this route.'); -// return this.timesheetsQuery.findAll(year, period_no, email); -// } - -// @Get('/:email') -// async getByEmail( -// @Param('email') email: string, -// @Query('offset') offset?: string, -// ): Promise { -// const week_offset = Number.isFinite(Number(offset)) ? Number(offset) : 0; -// return this.timesheetsQuery.getTimesheetByEmail(email, week_offset); -// } - -// @Post('shifts/:email') -// async createTimesheetShifts( -// @Param('email') email: string, -// @Body() dto: CreateWeekShiftsDto, -// @Query('offset') offset?: string, -// ): Promise { -// const week_offset = Number.isFinite(Number(offset)) ? Number(offset) : 0; -// return this.timesheetsCommand.createWeekShiftsAndReturnOverview(email, dto.shifts, week_offset); -// } -// } diff --git a/src/~misc_deprecated-files/update-expense.dto.ts b/src/~misc_deprecated-files/update-expense.dto.ts deleted file mode 100644 index c9f1ef6..0000000 --- a/src/~misc_deprecated-files/update-expense.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -// import { PartialType } from "@nestjs/swagger"; -// import { CreateExpenseDto } from "./create-expense.dto"; - -// export class UpdateExpenseDto extends PartialType(CreateExpenseDto) {} \ No newline at end of file diff --git a/src/~misc_deprecated-files/upsert-expense.dto.ts b/src/~misc_deprecated-files/upsert-expense.dto.ts deleted file mode 100644 index 3991aed..0000000 --- a/src/~misc_deprecated-files/upsert-expense.dto.ts +++ /dev/null @@ -1,59 +0,0 @@ -// import { Transform, Type } from "class-transformer"; -// import { -// IsNumber, -// IsOptional, -// IsString, -// Matches, -// MaxLength, -// Min, -// ValidateIf, -// ValidateNested -// } from "class-validator"; - -// export class ExpensePayloadDto { -// @IsString() -// type!: string; - -// @ValidateIf(o => (o.type ?? '').toUpperCase() !== 'MILEAGE') -// @IsNumber() -// @Min(0) -// amount?: number; - -// @ValidateIf(o => (o.type ?? '').toUpperCase() === 'MILEAGE') -// @IsNumber() -// @Min(0) -// mileage?: number; - -// @IsString() -// @MaxLength(280) -// @Transform(({ value }) => (typeof value === 'string' ? value.trim() : value)) -// comment!: string; - -// @IsOptional() -// @Transform(({ value }) => { -// if (value === null || value === undefined || value === '') return undefined; -// if (typeof value === 'number') return value.toString(); -// if (typeof value === 'string') { -// const trimmed = value.trim(); -// return trimmed.length ? trimmed : undefined; -// } -// return undefined; -// }) -// @IsString() -// @Matches(/^\d+$/) -// @MaxLength(255) -// attachment?: string; -// } - - -// export class UpsertExpenseDto { -// @IsOptional() -// @ValidateNested() -// @Type(()=> ExpensePayloadDto) -// old_expense?: ExpensePayloadDto; - -// @IsOptional() -// @ValidateNested() -// @Type(()=> ExpensePayloadDto) -// new_expense?: ExpensePayloadDto; -// } \ No newline at end of file diff --git a/src/~misc_deprecated-files/upsert-shift.dto.ts b/src/~misc_deprecated-files/upsert-shift.dto.ts deleted file mode 100644 index eab1abe..0000000 --- a/src/~misc_deprecated-files/upsert-shift.dto.ts +++ /dev/null @@ -1,43 +0,0 @@ -// import { Type } from "class-transformer"; -// import { IsBoolean, IsOptional, IsString, Matches, MaxLength, ValidateNested } from "class-validator"; - -// export const COMMENT_MAX_LENGTH = 280; - -// export class ShiftPayloadDto { - -// @Matches(/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/) -// date!: string; - -// @Matches(/^([01]\d|2[0-3]):([0-5]\d)$/) -// start_time!: string; - -// @Matches(/^([01]\d|2[0-3]):([0-5]\d)$/) -// end_time!: string; - -// @IsString() -// type!: string; - -// @IsBoolean() -// is_remote!: boolean; - -// @IsBoolean() -// is_approved!: boolean; - -// @IsOptional() -// @IsString() -// @MaxLength(COMMENT_MAX_LENGTH) -// comment?: string; -// }; - -// export class UpsertShiftDto { - -// @IsOptional() -// @ValidateNested() -// @Type(()=> ShiftPayloadDto) -// old_shift?: ShiftPayloadDto; - -// @IsOptional() -// @ValidateNested() -// @Type(()=> ShiftPayloadDto) -// new_shift?: ShiftPayloadDto; -// }; \ No newline at end of file diff --git a/src/~misc_deprecated-files/utils.constant.ts b/src/~misc_deprecated-files/utils.constant.ts deleted file mode 100644 index 005ac6c..0000000 --- a/src/~misc_deprecated-files/utils.constant.ts +++ /dev/null @@ -1 +0,0 @@ -// export const COMMENT_MAX_LENGTH = 280; \ No newline at end of file