From 9f0da467ae0ba394f927e1b77237ba491a1faf9a Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 31 Oct 2025 08:50:13 -0400 Subject: [PATCH 01/12] fix(timesheet): removed validation/transformation from ensureTimesheet and use the param Date format inside the function --- .../services/timesheet-get-overview.service.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts index e89d684..81bc330 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -178,11 +178,10 @@ export class GetTimesheetsOverviewService { }; } - private ensureTimesheet = async (employee_id: number, start_date: Date | string) => { - const start = toDateFromString(start_date); + private ensureTimesheet = async (employee_id: number, start_date: Date) => { let row = await this.prisma.timesheets.findFirst({ - where: { employee_id, start_date: start }, + where: { employee_id, start_date: start_date }, include: { employee: { include: { user: true } }, shift: { include: { bank_code: true } }, @@ -194,13 +193,13 @@ export class GetTimesheetsOverviewService { await this.prisma.timesheets.create({ data: { employee_id, - start_date: start, + start_date: start_date, is_approved: false }, }); row = await this.prisma.timesheets.findFirst({ - where: { employee_id, start_date: start }, + where: { employee_id, start_date: start_date }, include: { employee: { include: { user: true } }, shift: { include: { bank_code: true } }, From e0cefc8ec92e111946170a38910de6a928b395af Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 31 Oct 2025 12:11:55 -0400 Subject: [PATCH 02/12] refactor(expenses): modified createExpense signature. removed timesheet_id from the param. ajusted auth logic --- docs/swagger/swagger-spec.json | 17 ++++------------- .../controllers/auth.controller.ts | 3 ++- src/main.ts | 2 +- .../expenses/controllers/expense.controller.ts | 12 +++++------- .../expenses/services/expense-upsert.service.ts | 11 ++++++++--- .../services/timesheet-get-overview.service.ts | 9 +++++---- .../utils/date-time.utils.ts | 2 +- 7 files changed, 26 insertions(+), 30 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index e9c9b27..ae43cd0 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -605,19 +605,10 @@ ] } }, - "/expense/{timesheet_id}": { + "/expense/create": { "post": { "operationId": "ExpenseController_create", - "parameters": [ - { - "name": "timesheet_id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], + "parameters": [], "requestBody": { "required": true, "content": { @@ -638,7 +629,7 @@ ] } }, - "/expense": { + "/expense/update": { "patch": { "operationId": "ExpenseController_update", "parameters": [], @@ -652,7 +643,7 @@ ] } }, - "/expense/{expense_id}": { + "/expense/delete/{expense_id}": { "delete": { "operationId": "ExpenseController_remove", "parameters": [ diff --git a/src/identity-and-account/authentication/controllers/auth.controller.ts b/src/identity-and-account/authentication/controllers/auth.controller.ts index 0ec1ff2..43c9397 100644 --- a/src/identity-and-account/authentication/controllers/auth.controller.ts +++ b/src/identity-and-account/authentication/controllers/auth.controller.ts @@ -12,7 +12,8 @@ export class AuthController { @Get('/callback') @UseGuards(OIDCLoginGuard) loginCallback(@Req() req: Request, @Res() res: Response) { - res.redirect('http://10.100.251.2:9011/#/login-success'); + // res.redirect('http://10.100.251.2:9011/#/login-success'); + res.redirect('http://localhost:9000/#/login-success'); } @Get('/me') diff --git a/src/main.ts b/src/main.ts index fb35236..7c06222 100644 --- a/src/main.ts +++ b/src/main.ts @@ -46,7 +46,7 @@ async function bootstrap() { // Enable CORS app.enableCors({ - origin: ['http://10.100.251.2:9011', 'http://10.100.251.2:9012', 'http://10.100.251.2:9013'], + origin: ['http://10.100.251.2:9011', 'http://10.100.251.2:9012', 'http://10.100.251.2:9013', 'http://localhost:9000'], credentials: true, }); diff --git a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts b/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts index 572972d..6e4f495 100644 --- a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts @@ -9,20 +9,18 @@ import { ExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expens export class ExpenseController { constructor( private readonly upsert_service: ExpenseUpsertService ){} - @Post(':timesheet_id') - create( - @Param('timesheet_id', ParseIntPipe) timesheet_id: number, - @Body() dto: ExpenseDto): Promise{ - return this.upsert_service.createExpense(timesheet_id, dto); + @Post('create') + create(@Body() dto: ExpenseDto): Promise{ + return this.upsert_service.createExpense(dto); } - @Patch() + @Patch('update') update( @Body() body: { update :{ id: number; dto: updateExpenseDto }}): Promise{ return this.upsert_service.updateExpense(body.update); } - @Delete(':expense_id') + @Delete('delete/:expense_id') remove(@Param('expense_id') expense_id: number) { return this.upsert_service.deleteExpense(expense_id); } diff --git a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts index 64390af..38990a0 100644 --- a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts @@ -15,7 +15,7 @@ export class ExpenseUpsertService { //_________________________________________________________________ // CREATE //_________________________________________________________________ - async createExpense(timesheet_id: number, dto: ExpenseDto): Promise { + async createExpense( dto: ExpenseDto): Promise { try { //normalize strings and dates const normed_expense = this.normalizeExpenseDto(dto); @@ -24,11 +24,16 @@ export class ExpenseUpsertService { const parsed_amount = this.parseOptionalNumber(dto.amount, "amount"); const parsed_mileage = this.parseOptionalNumber(dto.mileage, "mileage"); const parsed_attachment = this.parseOptionalNumber(dto.attachment, "attachment"); - + + const timesheet = await this.prisma.timesheets.findUnique({ + where: { id: dto.timesheet_id }, + select: { id: true }, + }); + if(!timesheet) throw new NotFoundException(`Timesheet with id ${dto.timesheet_id} not found`); //create a new expense const expense = await this.prisma.expenses.create({ data: { - timesheet_id, + timesheet_id: timesheet.id, bank_code_id: dto.bank_code_id, attachment: parsed_attachment, date: normed_expense.date, diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts index 81bc330..e89d684 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -178,10 +178,11 @@ export class GetTimesheetsOverviewService { }; } - private ensureTimesheet = async (employee_id: number, start_date: Date) => { + private ensureTimesheet = async (employee_id: number, start_date: Date | string) => { + const start = toDateFromString(start_date); let row = await this.prisma.timesheets.findFirst({ - where: { employee_id, start_date: start_date }, + where: { employee_id, start_date: start }, include: { employee: { include: { user: true } }, shift: { include: { bank_code: true } }, @@ -193,13 +194,13 @@ export class GetTimesheetsOverviewService { await this.prisma.timesheets.create({ data: { employee_id, - start_date: start_date, + start_date: start, is_approved: false }, }); row = await this.prisma.timesheets.findFirst({ - where: { employee_id, start_date: start_date }, + where: { employee_id, start_date: start }, include: { employee: { include: { user: true } }, shift: { include: { bank_code: true } }, diff --git a/src/time-and-attendance/utils/date-time.utils.ts b/src/time-and-attendance/utils/date-time.utils.ts index c43337d..07ea016 100644 --- a/src/time-and-attendance/utils/date-time.utils.ts +++ b/src/time-and-attendance/utils/date-time.utils.ts @@ -40,7 +40,7 @@ export const toHHmmFromDate = (input: Date | string): string => { //converts Date format to string export const toDateFromString = (ymd: string | Date): Date => { - return new Date(`${ymd}T00:00:00:000Z`); + return new Date(`${ymd}`); } export const toUTCDateFromString = (iso: string | Date) => { From e5484da39a22c5dfe015d5306f1373c2524a166e Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 31 Oct 2025 12:34:12 -0400 Subject: [PATCH 03/12] refactor(expenses): added email to req inside controller and pass email to the function to pin point the right timesheet --- .../controllers/expense.controller.ts | 8 ++++--- .../services/expense-upsert.service.ts | 22 +++++++++++++------ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts b/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts index 6e4f495..594d583 100644 --- a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Post, Param, ParseIntPipe, Body, Patch, Delete } from "@nestjs/common"; +import { Controller, Post, Param, Body, Patch, Delete, Req, UnauthorizedException } from "@nestjs/common"; import { CreateExpenseResult, UpdateExpenseResult } from "src/time-and-attendance/utils/type.utils"; import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; import { updateExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-update.dto"; @@ -10,8 +10,10 @@ export class ExpenseController { constructor( private readonly upsert_service: ExpenseUpsertService ){} @Post('create') - create(@Body() dto: ExpenseDto): Promise{ - return this.upsert_service.createExpense(dto); + create( @Req() req, @Body() dto: ExpenseDto): Promise{ + const email = req.user?.email; + if(!email) throw new UnauthorizedException('Unauthorized User'); + return this.upsert_service.createExpense(dto, email); } @Patch('update') diff --git a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts index 38990a0..9687bca 100644 --- a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts @@ -1,22 +1,29 @@ import { CreateExpenseResult, UpdateExpensePayload, UpdateExpenseResult, DeleteExpenseResult, NormalizedExpense } from "src/time-and-attendance/utils/type.utils"; import { toDateFromString, toStringFromDate } from "src/time-and-attendance/utils/date-time.utils"; -import { Injectable, NotFoundException } from "@nestjs/common"; +import { Injectable, NotFoundException, Req } from "@nestjs/common"; import { expense_select } from "src/time-and-attendance/utils/selects.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { GetExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-get.dto"; import { ExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-create.dto"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; @Injectable() export class ExpenseUpsertService { - constructor(private readonly prisma: PrismaService) { } + constructor( + private readonly prisma: PrismaService, + private readonly emailResolver: EmailToIdResolver, + ) { } //_________________________________________________________________ // CREATE //_________________________________________________________________ - async createExpense( dto: ExpenseDto): Promise { + async createExpense( dto: ExpenseDto, email: string): Promise { try { + //fetch employee_id using req.user.email + const employee_id = await this.emailResolver.findIdByEmail(email); + //normalize strings and dates const normed_expense = this.normalizeExpenseDto(dto); @@ -24,12 +31,13 @@ export class ExpenseUpsertService { const parsed_amount = this.parseOptionalNumber(dto.amount, "amount"); const parsed_mileage = this.parseOptionalNumber(dto.mileage, "mileage"); const parsed_attachment = this.parseOptionalNumber(dto.attachment, "attachment"); - - const timesheet = await this.prisma.timesheets.findUnique({ - where: { id: dto.timesheet_id }, - select: { id: true }, + + const timesheet = await this.prisma.timesheets.findFirst({ + where: { id: dto.timesheet_id, employee_id: employee_id }, + select: { id: true, employee_id: true }, }); if(!timesheet) throw new NotFoundException(`Timesheet with id ${dto.timesheet_id} not found`); + //create a new expense const expense = await this.prisma.expenses.create({ data: { From 6c746aa3c20e1ab553888c713111c080338df43a Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 31 Oct 2025 12:37:11 -0400 Subject: [PATCH 04/12] refactor(expenses): added start_date to find the right timesheet using expense.date --- .../modules/expenses/services/expense-upsert.service.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts index 9687bca..d2c4cb7 100644 --- a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts @@ -1,6 +1,6 @@ import { CreateExpenseResult, UpdateExpensePayload, UpdateExpenseResult, DeleteExpenseResult, NormalizedExpense } from "src/time-and-attendance/utils/type.utils"; -import { toDateFromString, toStringFromDate } from "src/time-and-attendance/utils/date-time.utils"; +import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils"; import { Injectable, NotFoundException, Req } from "@nestjs/common"; import { expense_select } from "src/time-and-attendance/utils/selects.utils"; import { PrismaService } from "src/prisma/prisma.service"; @@ -27,13 +27,16 @@ export class ExpenseUpsertService { //normalize strings and dates const normed_expense = this.normalizeExpenseDto(dto); + //finds the timesheet using expense.date + const start_date = weekStartSunday(normed_expense.date); + //parse numbers const parsed_amount = this.parseOptionalNumber(dto.amount, "amount"); const parsed_mileage = this.parseOptionalNumber(dto.mileage, "mileage"); const parsed_attachment = this.parseOptionalNumber(dto.attachment, "attachment"); const timesheet = await this.prisma.timesheets.findFirst({ - where: { id: dto.timesheet_id, employee_id: employee_id }, + where: { start_date, employee_id }, select: { id: true, employee_id: true }, }); if(!timesheet) throw new NotFoundException(`Timesheet with id ${dto.timesheet_id} not found`); From bb60887a0d82c4ef31142643b1ad8bfa2b717649 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 31 Oct 2025 14:04:07 -0400 Subject: [PATCH 05/12] refactor(timesheet): used session data and removed email from query of the Get function --- docs/swagger/swagger-spec.json | 12 ++-------- .../controllers/timesheet.controller.ts | 24 +++++++------------ .../timesheet-get-overview.service.ts | 10 ++++++-- 3 files changed, 18 insertions(+), 28 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index ae43cd0..af88b3e 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -342,20 +342,12 @@ "get": { "operationId": "TimesheetController_getTimesheetByIds", "parameters": [ - { - "name": "employee_email", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - }, { "name": "year", "required": true, "in": "query", "schema": { - "type": "string" + "type": "number" } }, { @@ -363,7 +355,7 @@ "required": true, "in": "query", "schema": { - "type": "string" + "type": "number" } } ], diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts index 9ec6d6e..414930a 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts @@ -1,26 +1,18 @@ +import { Controller, Get, ParseIntPipe, Query, Req, UnauthorizedException} from "@nestjs/common"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service"; -import { BadRequestException, Controller, Get, Query} from "@nestjs/common"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; @Controller('timesheets') export class TimesheetController { - constructor( - private readonly timesheetOverview: GetTimesheetsOverviewService, - private readonly emailResolver: EmailToIdResolver, - ){} + constructor( private readonly timesheetOverview: GetTimesheetsOverviewService ){} @Get() async getTimesheetByIds( - @Query('employee_email') employee_email: string, - @Query('year') year: string, - @Query('period_number') period_number: string, + @Req() req, + @Query('year', ParseIntPipe) year: number, + @Query('period_number', ParseIntPipe) period_number: number, ) { - if (!employee_email || !year || !period_number) { - throw new BadRequestException('Query params "employee_email", "year" and eriod_number" are required.'); - } - const employee_id = await this.emailResolver.findIdByEmail(employee_email); - const pay_year = Number(year); - const period_num = Number(period_number); - return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(employee_id, pay_year, period_num); + const email = req.user?.email; + if(!email) throw new UnauthorizedException('Unauthorized User'); + return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(email, year, period_number); } } diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts index e89d684..c6da872 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -3,19 +3,25 @@ import { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/time-and-attendance/utils/co import { Injectable, NotFoundException } from "@nestjs/common"; import { TotalExpenses, TotalHours } from "src/time-and-attendance/utils/type.utils"; import { PrismaService } from "src/prisma/prisma.service"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; @Injectable() export class GetTimesheetsOverviewService { - constructor(private readonly prisma: PrismaService) { } + constructor( + private readonly prisma: PrismaService, + private readonly emailResolver : EmailToIdResolver, + ) { } //----------------------------------------------------------------------------------- // GET TIMESHEETS FOR A SELECTED EMPLOYEE //----------------------------------------------------------------------------------- - async getTimesheetsForEmployeeByPeriod(employee_id: number, pay_year: number, pay_period_no: number) { + async getTimesheetsForEmployeeByPeriod(email: string, pay_year: number, pay_period_no: number) { //find period using year and period_no const period = await this.prisma.payPeriods.findFirst({ where: { pay_year, pay_period_no } }); if (!period) throw new NotFoundException(`Pay period ${pay_year}-${pay_period_no} not found`); + //fetch the employee_id using the email + const employee_id = await this.emailResolver.findIdByEmail(email); //loads the timesheets related to the fetched pay-period const timesheet_range = { employee_id, start_date: { gte: period.period_start, lte: period.period_end } }; let rows = await this.loadTimesheets(timesheet_range); From c274550a91dfd44bfae2a58e29fe2e59ed5ae9ca Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 3 Nov 2025 10:15:40 -0500 Subject: [PATCH 06/12] refactor(shifts): removed email from param of create shift and used req-user data instead --- .../shifts/controllers/shift.controller.ts | 8 +- .../shifts/services/shifts-upsert.service.ts | 151 +++++++++++++----- 2 files changed, 114 insertions(+), 45 deletions(-) diff --git a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts index 6928cc8..e26891b 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Body, Controller, Delete, Param, Patch, Post } from "@nestjs/common"; +import { BadRequestException, Body, Controller, Delete, Param, Patch, Post, Req } from "@nestjs/common"; import { CreateShiftResult, UpdateShiftResult } from "src/time-and-attendance/utils/type.utils"; import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; @@ -10,11 +10,13 @@ export class ShiftController { constructor( private readonly upsert_service: ShiftsUpsertService ){} @Post('create') - createBatch( + createBatch( + @Req() req, @Body()dtos: ShiftDto[]): Promise { + const email = req.user?.email; const list = Array.isArray(dtos) ? dtos : []; if(list.length === 0) throw new BadRequestException('Body is missing or invalid (create shifts)'); - return this.upsert_service.createShifts(dtos) + return this.upsert_service.createShifts(email, dtos) } diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts index e16797c..5a1e6c4 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts @@ -1,5 +1,5 @@ import { CreateShiftResult, NormedOk, NormedErr, UpdateShiftResult, UpdateShiftPayload, UpdateShiftChanges, Normalized } from "src/time-and-attendance/utils/type.utils"; -import { overlaps, toStringFromHHmm, toStringFromDate, toDateFromString, toHHmmFromString } from "src/time-and-attendance/utils/date-time.utils"; +import { overlaps, toStringFromHHmm, toStringFromDate, toDateFromString, toHHmmFromString, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils"; import { Injectable, BadRequestException, ConflictException, NotFoundException } from "@nestjs/common"; import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; @@ -7,6 +7,7 @@ import { PrismaService } from "src/prisma/prisma.service"; import { shift_select } from "src/time-and-attendance/utils/selects.utils"; import { GetShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto"; import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; +import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; @@ -15,6 +16,7 @@ export class ShiftsUpsertService { constructor( private readonly prisma: PrismaService, private readonly overtime: OvertimeService, + private readonly emailResolver: EmailToIdResolver, ) { } //_________________________________________________________________ @@ -25,76 +27,140 @@ export class ShiftsUpsertService { //checks for overlaping shifts //create new shifts //calculate overtime - async createShifts(dtos: ShiftDto[]): Promise { + async createShifts(email: string, dtos: ShiftDto[]): Promise { if (!Array.isArray(dtos) || dtos.length === 0) return []; - const normed_shift: Array = dtos.map((dto, index) => { - try { - const normed = this.normalizeShiftDto(dto); - if (normed.end_time <= normed.start_time) { - return { index, error: new BadRequestException(`end_time must be greater than start_time (index ${index})`) }; + const employee_id = await this.emailResolver.findIdByEmail(email); + + const normed_shifts = await Promise.all( + dtos.map(async (dto, index) => { + try { + const normed = this.normalizeShiftDto(dto); + if (normed.end_time <= normed.start_time) { + return { + index, + error: new BadRequestException( + `end_time must be greater than start_time (index ${index})` + ), + }; + } + + const start_date = weekStartSunday(normed.date); + + const timesheet = await this.prisma.timesheets.findFirst({ + where: { start_date, employee_id }, + select: { id: true }, + }); + if (!timesheet) { + return { + index, + error: new NotFoundException(`Timesheet not found`), + }; + + } + + return { + index, + dto, + normed, + timesheet_id: timesheet.id, + }; + } catch (error) { + return { index, error }; } - return { index, dto, normed }; - } catch (error) { - return { index, error }; - } - }); - const ok_items = normed_shift.filter((x): x is NormedOk => "normed" in x); + })); - const regroup_by_date = new Map(); + const ok_items = normed_shifts.filter( + (item): item is NormedOk & { timesheet_id: number } => "normed" in item); - ok_items.forEach(({ index, normed }) => { - const d = normed.date; - const key = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); + const regroup_by_date = new Map(); + ok_items.forEach(({ index, normed, timesheet_id }) => { + const day = new Date(normed.date.getFullYear(), normed.date.getMonth(), normed.date.getDate()).getTime(); + const key = `${timesheet_id}|${day}`; if (!regroup_by_date.has(key)) regroup_by_date.set(key, []); regroup_by_date.get(key)!.push(index); }); + const timesheet_keys = Array.from(regroup_by_date.keys()).map((raw) => { + const [timesheet, day] = raw.split('|'); + return { + timesheet_id: Number(timesheet), + day: Number(day), + key: raw, + }; + }); + for (const indices of regroup_by_date.values()) { const ordered = indices .map(index => { - const item = normed_shift[index] as NormedOk; - return { index: index, start: item.normed.start_time, end: item.normed.end_time }; + const item = normed_shifts[index] as NormedOk & { timesheet_id: number }; + return { + index: index, + start: item.normed.start_time, + end: item.normed.end_time + }; }) .sort((a, b) => a.start.getTime() - b.start.getTime()); for (let j = 1; j < ordered.length; j++) { - if (overlaps({ start: ordered[j - 1].start, end: ordered[j - 1].end }, { start: ordered[j].start, end: ordered[j].end })) { - const err = new ConflictException({ + if ( + overlaps( + { start: ordered[j - 1].start, end: ordered[j - 1].end }, + { start: ordered[j].start, end: ordered[j].end } + ) + ) { + const error = new ConflictException({ error_code: 'SHIFT_OVERLAP_BATCH', message: 'New shift overlaps with another shift in the same batch (same day).', }); return dtos.map((_dto, key) => indices.includes(key) - ? ({ ok: false, error: err } as CreateShiftResult) - : ({ ok: false, error: new BadRequestException('Batch aborted due to overlaps in another date group') }) + ? ({ + ok: false, + error + } as CreateShiftResult) + : ({ + ok: false, + error: new BadRequestException( + 'Batch aborted due to overlaps in another date group' + ), + }), ); } } } return this.prisma.$transaction(async (tx) => { - const results: CreateShiftResult[] = Array.from({ length: dtos.length }, () => ({ ok: false, error: new Error('uninitialized') })); + const results: CreateShiftResult[] = Array.from( + { length: dtos.length }, + () => ({ ok: false, error: new Error('uninitialized') })); + const existing_map = new Map(); - normed_shift.forEach((x, i) => { + for (const { timesheet_id, day, key } of timesheet_keys) { + const day_date = new Date(day); + const rows = await tx.shifts.findMany({ + where: { timesheet_id, date: day_date }, + select: { start_time: true, end_time: true }, + }); + existing_map.set( + key, + rows.map((row) => ({ start_time: row.start_time, end_time: row.end_time })), + ); + } + + normed_shifts.forEach((x, i) => { if ("error" in x) results[i] = { ok: false, error: x.error }; }); - const unique_dates = Array.from(regroup_by_date.keys()).map(ms => new Date(ms)); - const existing_date = new Map(); - for (const d of unique_dates) { - const rows = await tx.shifts.findMany({ - where: { date: d }, - select: { start_time: true, end_time: true }, - }); - existing_date.set(d.getTime(), rows.map(r => ({ start_time: r.start_time, end_time: r.end_time }))); - } - for (const item of ok_items) { - const { index, dto, normed } = item; - const dayKey = new Date(normed.date.getFullYear(), normed.date.getMonth(), normed.date.getDate()).getTime(); - const existing = existing_date.get(dayKey) ?? []; - + const { index, dto, normed, timesheet_id } = item; + const day_key = new Date(normed.date.getFullYear(), normed.date.getMonth(), normed.date.getDate()).getTime(); + const map_key = `${timesheet_id}|${day_key}`; + let existing = existing_map.get(map_key); + if(!existing) { + existing = []; + existing_map.set(map_key, existing); + } const hit = existing.find(e => overlaps({ start: e.start_time, end: e.end_time }, { start: normed.start_time, end: normed.end_time })); if (hit) { results[index] = { @@ -114,7 +180,7 @@ export class ShiftsUpsertService { const row = await tx.shifts.create({ data: { - timesheet_id: dto.timesheet_id, + timesheet_id: timesheet_id, bank_code_id: dto.bank_code_id, date: normed.date, start_time: normed.start_time, @@ -126,10 +192,11 @@ export class ShiftsUpsertService { }); existing.push({ start_time: row.start_time, end_time: row.end_time }); + existing_map.set(map_key, existing); - const summary = await this.overtime.getWeekOvertimeSummary(dto.timesheet_id, normed.date, tx); + const summary = await this.overtime.getWeekOvertimeSummary(timesheet_id, normed.date, tx); const shift: GetShiftDto = { - timesheet_id: row.timesheet_id, + timesheet_id: timesheet_id, bank_code_id: row.bank_code_id, date: toStringFromDate(row.date), start_time: toStringFromHHmm(row.start_time), From b1c6c50571735ca9b850791ee88b4899a5060cd1 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 3 Nov 2025 10:53:15 -0500 Subject: [PATCH 07/12] refactor(time-and-attendance): removed a layer of folder --- package-lock.json | 49 +++++++++++++------ src/app.module.ts | 2 +- src/modules/exports/csv-exports.module.ts | 2 +- .../controllers/expense.controller.ts | 7 ++- .../expenses/dtos/expense-create.dto.ts | 0 .../expenses/dtos/expense-get.dto.ts | 0 .../expenses/dtos/expense-update.dto.ts | 2 +- .../expenses/expenses.module.ts | 10 ++++ .../services/expense-upsert.service.ts | 6 +-- .../services/expenses-archival.service.ts | 0 .../controllers/leave-requests.controller.ts | 3 +- .../dtos/leave-request-view.dto.ts | 0 .../dtos/upsert-leave-request.dto.ts | 0 .../leave-requests/leave-requests.module.ts | 9 ++-- .../mappers/leave-requests-archive.mapper.ts | 4 +- .../mappers/leave-requests.mapper.ts | 0 .../holiday-leave-requests.service.ts | 15 +++--- .../services/leave-request.service.ts | 20 ++++---- .../services/sick-leave-requests.service.ts | 19 +++---- .../vacation-leave-requests.service.ts | 18 +++---- .../utils/leave-request.transform.ts | 20 ++++++++ .../utils/leave-request.util.ts | 0 .../utils/leave-requests-archive.select.ts | 0 .../modules/expenses/expenses.module.ts | 10 ---- .../utils/leave-request.transform.ts | 19 ------- .../dtos/update-schedule-presets.dto.ts | 3 -- .../time-tracker/shifts/shifts.module.ts | 14 ------ .../controllers/pay-periods.controller.ts | 0 .../pay-period/dtos/bulk-crew-approval.dto.ts | 0 .../pay-period/dtos/bundle-pay-period.dto.ts | 0 .../dtos/overview-employee-period.dto.ts | 0 .../dtos/overview-pay-period.dto.ts | 0 .../pay-period/dtos/pay-period.dto.ts | 0 .../pay-period/mappers/pay-periods.mapper.ts | 0 .../pay-period/pay-periods.module.ts | 0 .../services/pay-periods-command.service.ts | 0 .../services/pay-periods-query.service.ts | 0 .../shared/helpers/date-time.helpers.ts | 0 .../shared/interfaces/shifts.interface.ts | 0 .../shared/selects/expenses.select.ts | 0 .../shared/selects/pay-periods.select.ts | 0 .../shared/selects/shifts.select.ts | 0 .../{modules => }/shared/shared.module.ts | 0 .../utils/resolve-bank-type-id.utils.ts | 0 .../shared/utils/resolve-email-id.utils.ts | 0 .../shared/utils/resolve-full-name.utils.ts | 0 .../shared/utils/resolve-shifts-id.utils.ts | 0 .../shared/utils/resolve-timesheet.utils.ts | 0 .../time-and-attendance.module.ts | 29 +++++------ .../controller/schedule-presets.controller.ts | 11 +++-- .../dtos/create-schedule-preset-shifts.dto.ts | 0 .../dtos/create-schedule-presets.dto.ts | 2 +- .../dtos/update-schedule-presets.dto.ts | 4 ++ .../schedule-presets.module.ts | 11 +++-- .../schedule-presets-apply.service.ts | 0 .../services/schedule-presets-get.service.ts | 0 .../schedule-presets-upsert.service.ts | 4 +- .../shifts/controllers/shift.controller.ts | 6 +-- .../shifts/dtos/shift-create.dto.ts | 0 .../time-tracker/shifts/dtos/shift-get.dto.ts | 0 .../shifts/dtos/shift-update.dto.ts | 2 +- .../services/shifts-archival.service.ts | 0 .../shifts/services/shifts-get.service.ts | 2 +- .../shifts/services/shifts-upsert.service.ts | 8 +-- .../time-tracker/shifts/shifts.module.ts | 14 ++++++ .../controllers/timesheet.controller.ts | 2 +- .../timesheets/dtos/timesheet.dto.ts | 0 .../services/timesheet-approval.service.ts | 0 .../services/timesheet-archive.service.ts | 0 .../timesheet-get-overview.service.ts | 2 +- .../timesheets/timesheets.module.ts | 9 ++-- src/time-and-attendance/utils/type.utils.ts | 17 ++++--- 72 files changed, 190 insertions(+), 165 deletions(-) rename src/time-and-attendance/{modules => }/expenses/controllers/expense.controller.ts (75%) rename src/time-and-attendance/{modules => }/expenses/dtos/expense-create.dto.ts (100%) rename src/time-and-attendance/{modules => }/expenses/dtos/expense-get.dto.ts (100%) rename src/time-and-attendance/{modules => }/expenses/dtos/expense-update.dto.ts (65%) create mode 100644 src/time-and-attendance/expenses/expenses.module.ts rename src/time-and-attendance/{modules => }/expenses/services/expense-upsert.service.ts (96%) rename src/time-and-attendance/{modules => }/expenses/services/expenses-archival.service.ts (100%) rename src/time-and-attendance/{modules => }/leave-requests/controllers/leave-requests.controller.ts (94%) rename src/time-and-attendance/{modules => }/leave-requests/dtos/leave-request-view.dto.ts (100%) rename src/time-and-attendance/{modules => }/leave-requests/dtos/upsert-leave-request.dto.ts (100%) rename src/time-and-attendance/{modules => }/leave-requests/leave-requests.module.ts (54%) rename src/time-and-attendance/{modules => }/leave-requests/mappers/leave-requests-archive.mapper.ts (100%) rename src/time-and-attendance/{modules => }/leave-requests/mappers/leave-requests.mapper.ts (100%) rename src/time-and-attendance/{modules => }/leave-requests/services/holiday-leave-requests.service.ts (82%) rename src/time-and-attendance/{modules => }/leave-requests/services/leave-request.service.ts (96%) rename src/time-and-attendance/{modules => }/leave-requests/services/sick-leave-requests.service.ts (88%) rename src/time-and-attendance/{modules => }/leave-requests/services/vacation-leave-requests.service.ts (88%) create mode 100644 src/time-and-attendance/leave-requests/utils/leave-request.transform.ts rename src/time-and-attendance/{modules => }/leave-requests/utils/leave-request.util.ts (100%) rename src/time-and-attendance/{modules => }/leave-requests/utils/leave-requests-archive.select.ts (100%) delete mode 100644 src/time-and-attendance/modules/expenses/expenses.module.ts delete mode 100644 src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts delete mode 100644 src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts delete mode 100644 src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts rename src/time-and-attendance/{modules => }/pay-period/controllers/pay-periods.controller.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/dtos/bulk-crew-approval.dto.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/dtos/bundle-pay-period.dto.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/dtos/overview-employee-period.dto.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/dtos/overview-pay-period.dto.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/dtos/pay-period.dto.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/mappers/pay-periods.mapper.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/pay-periods.module.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/services/pay-periods-command.service.ts (100%) rename src/time-and-attendance/{modules => }/pay-period/services/pay-periods-query.service.ts (100%) rename src/time-and-attendance/{modules => }/shared/helpers/date-time.helpers.ts (100%) rename src/time-and-attendance/{modules => }/shared/interfaces/shifts.interface.ts (100%) rename src/time-and-attendance/{modules => }/shared/selects/expenses.select.ts (100%) rename src/time-and-attendance/{modules => }/shared/selects/pay-periods.select.ts (100%) rename src/time-and-attendance/{modules => }/shared/selects/shifts.select.ts (100%) rename src/time-and-attendance/{modules => }/shared/shared.module.ts (100%) rename src/time-and-attendance/{modules => }/shared/utils/resolve-bank-type-id.utils.ts (100%) rename src/time-and-attendance/{modules => }/shared/utils/resolve-email-id.utils.ts (100%) rename src/time-and-attendance/{modules => }/shared/utils/resolve-full-name.utils.ts (100%) rename src/time-and-attendance/{modules => }/shared/utils/resolve-shifts-id.utils.ts (100%) rename src/time-and-attendance/{modules => }/shared/utils/resolve-timesheet.utils.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/schedule-presets/controller/schedule-presets.controller.ts (83%) rename src/time-and-attendance/{modules => }/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts (82%) create mode 100644 src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts rename src/time-and-attendance/{modules => }/time-tracker/schedule-presets/schedule-presets.module.ts (62%) rename src/time-and-attendance/{modules => }/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/schedule-presets/services/schedule-presets-get.service.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts (97%) rename src/time-and-attendance/{modules => }/time-tracker/shifts/controllers/shift.controller.ts (78%) rename src/time-and-attendance/{modules => }/time-tracker/shifts/dtos/shift-create.dto.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/shifts/dtos/shift-get.dto.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/shifts/dtos/shift-update.dto.ts (76%) rename src/time-and-attendance/{modules => }/time-tracker/shifts/services/shifts-archival.service.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/shifts/services/shifts-get.service.ts (95%) rename src/time-and-attendance/{modules => }/time-tracker/shifts/services/shifts-upsert.service.ts (97%) create mode 100644 src/time-and-attendance/time-tracker/shifts/shifts.module.ts rename src/time-and-attendance/{modules => }/time-tracker/timesheets/controllers/timesheet.controller.ts (90%) rename src/time-and-attendance/{modules => }/time-tracker/timesheets/dtos/timesheet.dto.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/timesheets/services/timesheet-approval.service.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/timesheets/services/timesheet-archive.service.ts (100%) rename src/time-and-attendance/{modules => }/time-tracker/timesheets/services/timesheet-get-overview.service.ts (99%) rename src/time-and-attendance/{modules => }/time-tracker/timesheets/timesheets.module.ts (50%) diff --git a/package-lock.json b/package-lock.json index b3363fc..9b49166 100644 --- a/package-lock.json +++ b/package-lock.json @@ -243,6 +243,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -3113,6 +3114,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -3271,6 +3273,7 @@ "version": "11.1.7", "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.7.tgz", "integrity": "sha512-lwlObwGgIlpXSXYOTpfzdCepUyWomz6bv9qzGzzvpgspUxkj0Uz0fUJcvD44V8Ps7QhKW3lZBoYbXrH25UZrbA==", + "peer": true, "dependencies": { "file-type": "21.0.0", "iterare": "1.2.1", @@ -3316,6 +3319,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.7.tgz", "integrity": "sha512-TyXFOwjhHv/goSgJ8i20K78jwTM0iSpk9GBcC2h3mf4MxNy+znI8m7nWjfoACjTkb89cTwDQetfTHtSfGLLaiA==", "hasInstallScript": true, + "peer": true, "dependencies": { "@nuxt/opencollective": "0.4.1", "fast-safe-stringify": "2.1.1", @@ -3395,6 +3399,7 @@ "version": "11.1.7", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.7.tgz", "integrity": "sha512-5T+GLdvTiGPKB4/P4PM9ftKUKNHJy8ThEFhZA3vQnXVL7Vf0rDr07TfVTySVu+XTh85m1lpFVuyFM6u6wLNsRA==", + "peer": true, "dependencies": { "cors": "2.8.5", "express": "5.1.0", @@ -3794,6 +3799,7 @@ "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.6.0.tgz", "integrity": "sha512-Q5FsI3Cw0fGMXhmsg7c08i4EmXCrcl+WnAxb6LYOLHw4JFFC3yzmx9LaXZ7QMbA+JZXbigU2TirI7RAfO0Qlnw==", "dev": true, + "peer": true, "dependencies": { "@swc/counter": "^0.1.3", "@xhmikosr/bin-wrapper": "^13.0.5", @@ -3862,6 +3868,7 @@ "integrity": "sha512-CJSn2vstd17ddWIHBsjuD4OQnn9krQfaq6EO+w9YfId5DKznyPmzxAARlOXG99cC8/3Kli8ysKy6phL43bSr0w==", "dev": true, "hasInstallScript": true, + "peer": true, "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" @@ -4198,6 +4205,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -4357,6 +4365,7 @@ "version": "22.17.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.2.tgz", "integrity": "sha512-gL6z5N9Jm9mhY+U2KXZpteb+09zyffliRkZyZOHODGATyC5B1Jt/7TzuuiLkFsSUMLbS1OLmlj/E+/3KF4Q/4w==", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -4537,6 +4546,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.37.0", "@typescript-eslint/types": "8.37.0", @@ -5440,6 +5450,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5452,7 +5463,6 @@ "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, - "peer": true, "engines": { "node": ">=10.13.0" }, @@ -5486,6 +5496,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5953,6 +5964,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", @@ -6231,6 +6243,7 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "devOptional": true, + "peer": true, "dependencies": { "readdirp": "^4.0.1" }, @@ -6283,12 +6296,14 @@ "node_modules/class-transformer": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "peer": true }, "node_modules/class-validator": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.2.tgz", "integrity": "sha512-3kMVRF2io8N8pY1IFIXlho9r8IPUUIfHe2hYVtiebvAzU2XeQFXTv+XI4WX+TnXmtwXMDcjngcpkiPM0O9PvLw==", + "peer": true, "dependencies": { "@types/validator": "^13.11.8", "libphonenumber-js": "^1.11.1", @@ -7151,6 +7166,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -7211,6 +7227,7 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", "dev": true, + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -8717,6 +8734,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -10353,6 +10371,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "peer": true, "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1", @@ -10646,6 +10665,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -10700,6 +10720,7 @@ "integrity": "sha512-bXWy3vTk8mnRmT+SLyZBQoC2vtV9Z8u7OHvEu+aULYxwiop/CPiFZ+F56KsNRNf35jw+8wcu8pmLsjxpBxAO9g==", "devOptional": true, "hasInstallScript": true, + "peer": true, "dependencies": { "@prisma/config": "6.18.0", "@prisma/engines": "6.18.0" @@ -10919,7 +10940,8 @@ "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "peer": true }, "node_modules/repeat-string": { "version": "1.6.1", @@ -11108,6 +11130,7 @@ "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -11917,6 +11940,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -12225,6 +12249,7 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -12382,6 +12407,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "devOptional": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12564,9 +12590,10 @@ } }, "node_modules/validator": { - "version": "13.15.15", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", - "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", + "version": "13.15.20", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz", + "integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==", + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -12711,7 +12738,6 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, - "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -12729,7 +12755,6 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -12742,7 +12767,6 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -12756,7 +12780,6 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "peer": true, "engines": { "node": ">=4.0" } @@ -12765,15 +12788,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "peer": true + "dev": true }, "node_modules/webpack/node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, - "peer": true, "engines": { "node": ">= 0.6" } @@ -12783,7 +12804,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -12796,7 +12816,6 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", "dev": true, - "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", diff --git a/src/app.module.ts b/src/app.module.ts index ae2e569..dbe9dd9 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -14,8 +14,8 @@ import { ConfigModule } from '@nestjs/config'; import { APP_FILTER, APP_PIPE } from '@nestjs/core'; import { HttpExceptionFilter } from './common/filters/http-exception.filter'; import { ValidationError } from 'class-validator'; -import { PayperiodsModule } from './time-and-attendance/modules/pay-period/pay-periods.module'; import { TimeAndAttendanceModule } from 'src/time-and-attendance/time-and-attendance.module'; +import { PayperiodsModule } from 'src/time-and-attendance/pay-period/pay-periods.module'; @Module({ imports: [ diff --git a/src/modules/exports/csv-exports.module.ts b/src/modules/exports/csv-exports.module.ts index fa0c7f1..30a9b8d 100644 --- a/src/modules/exports/csv-exports.module.ts +++ b/src/modules/exports/csv-exports.module.ts @@ -1,7 +1,7 @@ import { Module } from "@nestjs/common"; import { CsvExportController } from "./controllers/csv-exports.controller"; import { CsvExportService } from "./services/csv-exports.service"; -import { SharedModule } from "../../time-and-attendance/modules/shared/shared.module"; +import { SharedModule } from "src/time-and-attendance/shared/shared.module"; @Module({ providers:[CsvExportService, SharedModule], diff --git a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts b/src/time-and-attendance/expenses/controllers/expense.controller.ts similarity index 75% rename from src/time-and-attendance/modules/expenses/controllers/expense.controller.ts rename to src/time-and-attendance/expenses/controllers/expense.controller.ts index 594d583..34b83e9 100644 --- a/src/time-and-attendance/modules/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/expenses/controllers/expense.controller.ts @@ -1,9 +1,8 @@ import { Controller, Post, Param, Body, Patch, Delete, Req, UnauthorizedException } from "@nestjs/common"; import { CreateExpenseResult, UpdateExpenseResult } from "src/time-and-attendance/utils/type.utils"; -import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; -import { updateExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-update.dto"; -import { ExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-create.dto"; - +import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service"; +import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto"; +import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; @Controller('expense') export class ExpenseController { diff --git a/src/time-and-attendance/modules/expenses/dtos/expense-create.dto.ts b/src/time-and-attendance/expenses/dtos/expense-create.dto.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/dtos/expense-create.dto.ts rename to src/time-and-attendance/expenses/dtos/expense-create.dto.ts diff --git a/src/time-and-attendance/modules/expenses/dtos/expense-get.dto.ts b/src/time-and-attendance/expenses/dtos/expense-get.dto.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/dtos/expense-get.dto.ts rename to src/time-and-attendance/expenses/dtos/expense-get.dto.ts diff --git a/src/time-and-attendance/modules/expenses/dtos/expense-update.dto.ts b/src/time-and-attendance/expenses/dtos/expense-update.dto.ts similarity index 65% rename from src/time-and-attendance/modules/expenses/dtos/expense-update.dto.ts rename to src/time-and-attendance/expenses/dtos/expense-update.dto.ts index 829f9c9..3dfe9b6 100644 --- a/src/time-and-attendance/modules/expenses/dtos/expense-update.dto.ts +++ b/src/time-and-attendance/expenses/dtos/expense-update.dto.ts @@ -1,5 +1,5 @@ import { OmitType, PartialType } from "@nestjs/swagger"; -import { ExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-create.dto"; +import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; export class updateExpenseDto extends PartialType ( OmitType(ExpenseDto, ['is_approved', 'timesheet_id'] as const) diff --git a/src/time-and-attendance/expenses/expenses.module.ts b/src/time-and-attendance/expenses/expenses.module.ts new file mode 100644 index 0000000..e6d737b --- /dev/null +++ b/src/time-and-attendance/expenses/expenses.module.ts @@ -0,0 +1,10 @@ +import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service"; +import { ExpenseController } from "src/time-and-attendance/expenses/controllers/expense.controller"; +import { Module } from "@nestjs/common"; + +@Module({ + controllers: [ ExpenseController ], + providers: [ ExpenseUpsertService ], +}) + +export class ExpensesModule {} \ No newline at end of file diff --git a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/expenses/services/expense-upsert.service.ts similarity index 96% rename from src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts rename to src/time-and-attendance/expenses/services/expense-upsert.service.ts index d2c4cb7..86a1c9b 100644 --- a/src/time-and-attendance/modules/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/expenses/services/expense-upsert.service.ts @@ -4,9 +4,9 @@ import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-an import { Injectable, NotFoundException, Req } from "@nestjs/common"; import { expense_select } from "src/time-and-attendance/utils/selects.utils"; import { PrismaService } from "src/prisma/prisma.service"; -import { GetExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-get.dto"; -import { ExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-create.dto"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; +import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; @Injectable() diff --git a/src/time-and-attendance/modules/expenses/services/expenses-archival.service.ts b/src/time-and-attendance/expenses/services/expenses-archival.service.ts similarity index 100% rename from src/time-and-attendance/modules/expenses/services/expenses-archival.service.ts rename to src/time-and-attendance/expenses/services/expenses-archival.service.ts diff --git a/src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller.ts b/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts similarity index 94% rename from src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller.ts rename to src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts index fc934ff..53c8e47 100644 --- a/src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller.ts +++ b/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts @@ -1,8 +1,7 @@ import { Body, Controller, Post } from "@nestjs/common"; import { ApiBearerAuth, ApiTags } from "@nestjs/swagger"; -import { LeaveRequestsService } from "../services/leave-request.service"; import { UpsertLeaveRequestDto } from "../dtos/upsert-leave-request.dto"; -import { LeaveTypes } from "@prisma/client"; +import { LeaveRequestsService } from "../services/leave-request.service"; @ApiTags('Leave Requests') @ApiBearerAuth('access-token') diff --git a/src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto.ts b/src/time-and-attendance/leave-requests/dtos/leave-request-view.dto.ts similarity index 100% rename from src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto.ts rename to src/time-and-attendance/leave-requests/dtos/leave-request-view.dto.ts diff --git a/src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto.ts b/src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto.ts similarity index 100% rename from src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto.ts rename to src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto.ts diff --git a/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts b/src/time-and-attendance/leave-requests/leave-requests.module.ts similarity index 54% rename from src/time-and-attendance/modules/leave-requests/leave-requests.module.ts rename to src/time-and-attendance/leave-requests/leave-requests.module.ts index 21b9e23..a822a27 100644 --- a/src/time-and-attendance/modules/leave-requests/leave-requests.module.ts +++ b/src/time-and-attendance/leave-requests/leave-requests.module.ts @@ -1,11 +1,10 @@ -import { LeaveRequestController } from "src/time-and-attendance/modules/leave-requests/controllers/leave-requests.controller"; -import { LeaveRequestsService } from "src/time-and-attendance/modules/leave-requests/services/leave-request.service"; +import { LeaveRequestController } from "src/time-and-attendance/leave-requests/controllers/leave-requests.controller"; +import { LeaveRequestsService } from "src/time-and-attendance/leave-requests/services/leave-request.service"; import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; -import { ShiftsModule } from "src/time-and-attendance/modules/time-tracker/shifts/shifts.module"; -import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; +import { SharedModule } from "src/time-and-attendance/shared/shared.module"; +import { ShiftsModule } from "src/time-and-attendance/time-tracker/shifts/shifts.module"; import { Module } from "@nestjs/common"; - @Module({ imports: [ BusinessLogicsModule, diff --git a/src/time-and-attendance/modules/leave-requests/mappers/leave-requests-archive.mapper.ts b/src/time-and-attendance/leave-requests/mappers/leave-requests-archive.mapper.ts similarity index 100% rename from src/time-and-attendance/modules/leave-requests/mappers/leave-requests-archive.mapper.ts rename to src/time-and-attendance/leave-requests/mappers/leave-requests-archive.mapper.ts index 36d05fa..6a17723 100644 --- a/src/time-and-attendance/modules/leave-requests/mappers/leave-requests-archive.mapper.ts +++ b/src/time-and-attendance/leave-requests/mappers/leave-requests-archive.mapper.ts @@ -1,6 +1,6 @@ -import { Prisma } from "@prisma/client"; -import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; import { LeaveRequestArchiveRow } from "../utils/leave-requests-archive.select"; +import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; +import { Prisma } from "@prisma/client"; const toNum = (value?: Prisma.Decimal | null) => value ? Number(value) : undefined; diff --git a/src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper.ts b/src/time-and-attendance/leave-requests/mappers/leave-requests.mapper.ts similarity index 100% rename from src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper.ts rename to src/time-and-attendance/leave-requests/mappers/leave-requests.mapper.ts diff --git a/src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts similarity index 82% rename from src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts rename to src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts index 148f4bf..5991686 100644 --- a/src/time-and-attendance/modules/leave-requests/services/holiday-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts @@ -1,15 +1,16 @@ import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; -import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto"; +import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto"; import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; -import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; -import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; -import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; -import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; +import { LeaveRequestsUtils } from "src/time-and-attendance/leave-requests/utils/leave-request.util"; +import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; import { PrismaService } from "src/prisma/prisma.service"; -import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper"; +import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; + @Injectable() diff --git a/src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts b/src/time-and-attendance/leave-requests/services/leave-request.service.ts similarity index 96% rename from src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts rename to src/time-and-attendance/leave-requests/services/leave-request.service.ts index 0fd0ceb..6390a10 100644 --- a/src/time-and-attendance/modules/leave-requests/services/leave-request.service.ts +++ b/src/time-and-attendance/leave-requests/services/leave-request.service.ts @@ -1,18 +1,18 @@ import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; -import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; -import { roundToQuarterHour } from "src/common/utils/date-utils"; +import { normalizeDates, toDateOnly, toISODateKey } from "src/time-and-attendance/shared/helpers/date-time.helpers"; import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto"; +import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; +import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; -import { mapRowToView } from "../mappers/leave-requests.mapper"; -import { PrismaService } from "src/prisma/prisma.service"; -import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; +import { roundToQuarterHour } from "src/common/utils/date-utils"; +import { LeaveRequestsUtils } from "src/time-and-attendance/leave-requests/utils/leave-request.util"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; -import { normalizeDates, toDateOnly, toISODateKey } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; -import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; -import { LeaveRequestsUtils } from "src/time-and-attendance/modules/leave-requests/utils/leave-request.util"; -import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; +import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; +import { PrismaService } from "src/prisma/prisma.service"; +import { mapRowToView } from "../mappers/leave-requests.mapper"; @Injectable() export class LeaveRequestsService { constructor( diff --git a/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts similarity index 88% rename from src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts rename to src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts index 969d8a5..629dbff 100644 --- a/src/time-and-attendance/modules/leave-requests/services/sick-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts @@ -1,15 +1,16 @@ import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; +import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto"; import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; -import { roundToQuarterHour } from "src/common/utils/date-utils"; -import { PrismaService } from "src/prisma/prisma.service"; -import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; -import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto"; -import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto"; -import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper"; -import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; -import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers"; +import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; +import { roundToQuarterHour } from "src/common/utils/date-utils"; +import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; +import { PrismaService } from "src/prisma/prisma.service"; +import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; + @Injectable() diff --git a/src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts similarity index 88% rename from src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts rename to src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts index 9f0afc7..d6f16a5 100644 --- a/src/time-and-attendance/modules/leave-requests/services/vacation-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts @@ -1,15 +1,15 @@ import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; +import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto"; import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; -import { roundToQuarterHour } from "src/common/utils/date-utils"; -import { PrismaService } from "src/prisma/prisma.service"; -import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; -import { LeaveRequestViewDto } from "src/time-and-attendance/modules/leave-requests/dtos/leave-request-view.dto"; -import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/modules/leave-requests/dtos/upsert-leave-request.dto"; -import { mapRowToView } from "src/time-and-attendance/modules/leave-requests/mappers/leave-requests.mapper"; -import { normalizeDates, toDateOnly } from "src/time-and-attendance/modules/shared/helpers/date-time.helpers"; -import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; +import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; +import { roundToQuarterHour } from "src/common/utils/date-utils"; +import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; +import { PrismaService } from "src/prisma/prisma.service"; +import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; @Injectable() diff --git a/src/time-and-attendance/leave-requests/utils/leave-request.transform.ts b/src/time-and-attendance/leave-requests/utils/leave-request.transform.ts new file mode 100644 index 0000000..5ff49fd --- /dev/null +++ b/src/time-and-attendance/leave-requests/utils/leave-request.transform.ts @@ -0,0 +1,20 @@ +import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; +import { mapArchiveRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests-archive.mapper"; +import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; +import { LeaveRequestArchiveRow } from "src/time-and-attendance/leave-requests/utils/leave-requests-archive.select"; +import { LeaveRequestRow } from "src/time-and-attendance/utils/type.utils"; + + +/** Active (table leave_requests) : proxy to base mapper */ +export function mapRowToViewWithDays(row: LeaveRequestRow): LeaveRequestViewDto { + return mapRowToView(row); +} + +/** Archive (table leave_requests_archive) : proxy to base mapper */ +export function mapArchiveRowToViewWithDays( + row: LeaveRequestArchiveRow, + email: string, + employee_full_name?: string, +): LeaveRequestViewDto { + return mapArchiveRowToView(row, email, employee_full_name!); +} \ No newline at end of file diff --git a/src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts b/src/time-and-attendance/leave-requests/utils/leave-request.util.ts similarity index 100% rename from src/time-and-attendance/modules/leave-requests/utils/leave-request.util.ts rename to src/time-and-attendance/leave-requests/utils/leave-request.util.ts diff --git a/src/time-and-attendance/modules/leave-requests/utils/leave-requests-archive.select.ts b/src/time-and-attendance/leave-requests/utils/leave-requests-archive.select.ts similarity index 100% rename from src/time-and-attendance/modules/leave-requests/utils/leave-requests-archive.select.ts rename to src/time-and-attendance/leave-requests/utils/leave-requests-archive.select.ts diff --git a/src/time-and-attendance/modules/expenses/expenses.module.ts b/src/time-and-attendance/modules/expenses/expenses.module.ts deleted file mode 100644 index 6ffff02..0000000 --- a/src/time-and-attendance/modules/expenses/expenses.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; -import { ExpenseController } from "src/time-and-attendance/modules/expenses/controllers/expense.controller"; -import { Module } from "@nestjs/common"; - -@Module({ - controllers: [ ExpenseController ], - providers: [ ExpenseUpsertService ], -}) - -export class ExpensesModule {} \ No newline at end of file diff --git a/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts b/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts deleted file mode 100644 index 042c796..0000000 --- a/src/time-and-attendance/modules/leave-requests/utils/leave-request.transform.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { LeaveRequestArchiveRow } from './leave-requests-archive.select'; -import { LeaveRequestViewDto } from '../dtos/leave-request-view.dto'; -import { mapArchiveRowToView } from '../mappers/leave-requests-archive.mapper'; -import { LeaveRequestRow } from 'src/time-and-attendance/utils/type.utils'; -import { mapRowToView } from '../mappers/leave-requests.mapper'; - -/** Active (table leave_requests) : proxy to base mapper */ -export function mapRowToViewWithDays(row: LeaveRequestRow): LeaveRequestViewDto { - return mapRowToView(row); -} - -/** Archive (table leave_requests_archive) : proxy to base mapper */ -export function mapArchiveRowToViewWithDays( - row: LeaveRequestArchiveRow, - email: string, - employee_full_name?: string, -): LeaveRequestViewDto { - return mapArchiveRowToView(row, email, employee_full_name!); -} \ No newline at end of file diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts b/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts deleted file mode 100644 index 2246c20..0000000 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; - -export class SchedulePresetsUpdateDto extends SchedulePresetsDto{} \ No newline at end of file diff --git a/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts b/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts deleted file mode 100644 index e031f31..0000000 --- a/src/time-and-attendance/modules/time-tracker/shifts/shifts.module.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import { BusinessLogicsModule } from 'src/time-and-attendance/domains/business-logics.module'; -import { ShiftsUpsertService } from 'src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service'; -import { ShiftsGetService } from 'src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service'; -import { ShiftController } from 'src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller'; -import { Module } from '@nestjs/common'; - -@Module({ - imports: [ BusinessLogicsModule ], - controllers: [ShiftController], - providers: [ ShiftsGetService, ShiftsUpsertService ], - exports: [ ShiftsUpsertService ], -}) -export class ShiftsModule {} diff --git a/src/time-and-attendance/modules/pay-period/controllers/pay-periods.controller.ts b/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/controllers/pay-periods.controller.ts rename to src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts diff --git a/src/time-and-attendance/modules/pay-period/dtos/bulk-crew-approval.dto.ts b/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/dtos/bulk-crew-approval.dto.ts rename to src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts diff --git a/src/time-and-attendance/modules/pay-period/dtos/bundle-pay-period.dto.ts b/src/time-and-attendance/pay-period/dtos/bundle-pay-period.dto.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/dtos/bundle-pay-period.dto.ts rename to src/time-and-attendance/pay-period/dtos/bundle-pay-period.dto.ts diff --git a/src/time-and-attendance/modules/pay-period/dtos/overview-employee-period.dto.ts b/src/time-and-attendance/pay-period/dtos/overview-employee-period.dto.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/dtos/overview-employee-period.dto.ts rename to src/time-and-attendance/pay-period/dtos/overview-employee-period.dto.ts diff --git a/src/time-and-attendance/modules/pay-period/dtos/overview-pay-period.dto.ts b/src/time-and-attendance/pay-period/dtos/overview-pay-period.dto.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/dtos/overview-pay-period.dto.ts rename to src/time-and-attendance/pay-period/dtos/overview-pay-period.dto.ts diff --git a/src/time-and-attendance/modules/pay-period/dtos/pay-period.dto.ts b/src/time-and-attendance/pay-period/dtos/pay-period.dto.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/dtos/pay-period.dto.ts rename to src/time-and-attendance/pay-period/dtos/pay-period.dto.ts diff --git a/src/time-and-attendance/modules/pay-period/mappers/pay-periods.mapper.ts b/src/time-and-attendance/pay-period/mappers/pay-periods.mapper.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/mappers/pay-periods.mapper.ts rename to src/time-and-attendance/pay-period/mappers/pay-periods.mapper.ts diff --git a/src/time-and-attendance/modules/pay-period/pay-periods.module.ts b/src/time-and-attendance/pay-period/pay-periods.module.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/pay-periods.module.ts rename to src/time-and-attendance/pay-period/pay-periods.module.ts diff --git a/src/time-and-attendance/modules/pay-period/services/pay-periods-command.service.ts b/src/time-and-attendance/pay-period/services/pay-periods-command.service.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/services/pay-periods-command.service.ts rename to src/time-and-attendance/pay-period/services/pay-periods-command.service.ts diff --git a/src/time-and-attendance/modules/pay-period/services/pay-periods-query.service.ts b/src/time-and-attendance/pay-period/services/pay-periods-query.service.ts similarity index 100% rename from src/time-and-attendance/modules/pay-period/services/pay-periods-query.service.ts rename to src/time-and-attendance/pay-period/services/pay-periods-query.service.ts diff --git a/src/time-and-attendance/modules/shared/helpers/date-time.helpers.ts b/src/time-and-attendance/shared/helpers/date-time.helpers.ts similarity index 100% rename from src/time-and-attendance/modules/shared/helpers/date-time.helpers.ts rename to src/time-and-attendance/shared/helpers/date-time.helpers.ts diff --git a/src/time-and-attendance/modules/shared/interfaces/shifts.interface.ts b/src/time-and-attendance/shared/interfaces/shifts.interface.ts similarity index 100% rename from src/time-and-attendance/modules/shared/interfaces/shifts.interface.ts rename to src/time-and-attendance/shared/interfaces/shifts.interface.ts diff --git a/src/time-and-attendance/modules/shared/selects/expenses.select.ts b/src/time-and-attendance/shared/selects/expenses.select.ts similarity index 100% rename from src/time-and-attendance/modules/shared/selects/expenses.select.ts rename to src/time-and-attendance/shared/selects/expenses.select.ts diff --git a/src/time-and-attendance/modules/shared/selects/pay-periods.select.ts b/src/time-and-attendance/shared/selects/pay-periods.select.ts similarity index 100% rename from src/time-and-attendance/modules/shared/selects/pay-periods.select.ts rename to src/time-and-attendance/shared/selects/pay-periods.select.ts diff --git a/src/time-and-attendance/modules/shared/selects/shifts.select.ts b/src/time-and-attendance/shared/selects/shifts.select.ts similarity index 100% rename from src/time-and-attendance/modules/shared/selects/shifts.select.ts rename to src/time-and-attendance/shared/selects/shifts.select.ts diff --git a/src/time-and-attendance/modules/shared/shared.module.ts b/src/time-and-attendance/shared/shared.module.ts similarity index 100% rename from src/time-and-attendance/modules/shared/shared.module.ts rename to src/time-and-attendance/shared/shared.module.ts diff --git a/src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils.ts b/src/time-and-attendance/shared/utils/resolve-bank-type-id.utils.ts similarity index 100% rename from src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils.ts rename to src/time-and-attendance/shared/utils/resolve-bank-type-id.utils.ts diff --git a/src/time-and-attendance/modules/shared/utils/resolve-email-id.utils.ts b/src/time-and-attendance/shared/utils/resolve-email-id.utils.ts similarity index 100% rename from src/time-and-attendance/modules/shared/utils/resolve-email-id.utils.ts rename to src/time-and-attendance/shared/utils/resolve-email-id.utils.ts diff --git a/src/time-and-attendance/modules/shared/utils/resolve-full-name.utils.ts b/src/time-and-attendance/shared/utils/resolve-full-name.utils.ts similarity index 100% rename from src/time-and-attendance/modules/shared/utils/resolve-full-name.utils.ts rename to src/time-and-attendance/shared/utils/resolve-full-name.utils.ts diff --git a/src/time-and-attendance/modules/shared/utils/resolve-shifts-id.utils.ts b/src/time-and-attendance/shared/utils/resolve-shifts-id.utils.ts similarity index 100% rename from src/time-and-attendance/modules/shared/utils/resolve-shifts-id.utils.ts rename to src/time-and-attendance/shared/utils/resolve-shifts-id.utils.ts diff --git a/src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts b/src/time-and-attendance/shared/utils/resolve-timesheet.utils.ts similarity index 100% rename from src/time-and-attendance/modules/shared/utils/resolve-timesheet.utils.ts rename to src/time-and-attendance/shared/utils/resolve-timesheet.utils.ts diff --git a/src/time-and-attendance/time-and-attendance.module.ts b/src/time-and-attendance/time-and-attendance.module.ts index ff54d41..89de655 100644 --- a/src/time-and-attendance/time-and-attendance.module.ts +++ b/src/time-and-attendance/time-and-attendance.module.ts @@ -1,18 +1,19 @@ -import { SchedulePresetsUpsertService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; -import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service"; -import { SchedulePresetsGetService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service"; -import { SchedulePresetsApplyService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service"; -import { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller"; -import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; -import { ExpenseUpsertService } from "src/time-and-attendance/modules/expenses/services/expense-upsert.service"; -import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; -import { TimesheetController } from "src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller"; -import { ExpenseController } from "src/time-and-attendance/modules/expenses/controllers/expense.controller"; -import { PayperiodsModule } from "src/time-and-attendance/modules/pay-period/pay-periods.module"; -import { ShiftsGetService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service"; -import { ShiftController } from "src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller"; -import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; + import { Module } from "@nestjs/common"; +import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; +import { ExpenseController } from "src/time-and-attendance/expenses/controllers/expense.controller"; +import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service"; +import { PayperiodsModule } from "src/time-and-attendance/pay-period/pay-periods.module"; +import { SharedModule } from "src/time-and-attendance/shared/shared.module"; +import { SchedulePresetsController } from "src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller"; +import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; +import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; +import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; +import { ShiftController } from "src/time-and-attendance/time-tracker/shifts/controllers/shift.controller"; +import { ShiftsGetService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-get.service"; +import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service"; +import { TimesheetController } from "src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller"; +import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service"; @Module({ imports: [ diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts similarity index 83% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts rename to src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index a9d5c87..3a2c692 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -1,9 +1,10 @@ import { Controller, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch } from "@nestjs/common"; -import { SchedulePresetsUpsertService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; -import { SchedulePresetsApplyService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service"; -import { SchedulePresetsGetService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service"; -import { SchedulePresetsUpdateDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/update-schedule-presets.dto"; -import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; +import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; +import { SchedulePresetsUpdateDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto"; +import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; +import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; +import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; + @Controller('schedule-presets') export class SchedulePresetsController { diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts b/src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts rename to src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts b/src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts similarity index 82% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts rename to src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts index 86d7704..fc6b7d8 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto.ts @@ -1,5 +1,5 @@ import { ArrayMinSize, IsArray, IsBoolean, IsOptional, IsString } from "class-validator"; -import { SchedulePresetShiftsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto"; +import { SchedulePresetShiftsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-preset-shifts.dto"; export class SchedulePresetsDto { @IsString() diff --git a/src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts b/src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts new file mode 100644 index 0000000..b8b9cb6 --- /dev/null +++ b/src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto.ts @@ -0,0 +1,4 @@ +import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; + + +export class SchedulePresetsUpdateDto extends SchedulePresetsDto{} \ No newline at end of file diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts b/src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts similarity index 62% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts rename to src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts index ac6c595..b261a2f 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/schedule-presets.module.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts @@ -1,9 +1,10 @@ -import { SchedulePresetsUpsertService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; -import { SchedulePresetsApplyService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service"; -import { SchedulePresetsGetService } from "src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service"; -import { SchedulePresetsController } from "src/time-and-attendance/modules/time-tracker/schedule-presets/controller/schedule-presets.controller"; -import { SharedModule } from "src/time-and-attendance/modules/shared/shared.module"; + import { Module } from "@nestjs/common"; +import { SharedModule } from "src/time-and-attendance/shared/shared.module"; +import { SchedulePresetsController } from "src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller"; +import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; +import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; +import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; @Module({ diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts rename to src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-get.service.ts rename to src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts diff --git a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts similarity index 97% rename from src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts rename to src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts index da45941..ec60558 100644 --- a/src/time-and-attendance/modules/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts @@ -1,10 +1,10 @@ import { Injectable, BadRequestException, NotFoundException, ConflictException } from "@nestjs/common"; import { CreatePresetResult, DeletePresetResult, UpdatePresetResult } from "src/time-and-attendance/utils/type.utils"; -import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; -import { BankCodesResolver } from "src/time-and-attendance/modules/shared/utils/resolve-bank-type-id.utils"; import { Prisma, Weekday } from "@prisma/client"; import { toHHmmFromDate } from "src/time-and-attendance/utils/date-time.utils"; import { PrismaService } from "src/prisma/prisma.service"; +import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; +import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; @Injectable() export class SchedulePresetsUpsertService { diff --git a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts similarity index 78% rename from src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts rename to src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts index e26891b..07395f9 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts @@ -1,8 +1,8 @@ import { BadRequestException, Body, Controller, Delete, Param, Patch, Post, Req } from "@nestjs/common"; +import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; +import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; +import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service"; import { CreateShiftResult, UpdateShiftResult } from "src/time-and-attendance/utils/type.utils"; -import { ShiftsUpsertService } from "src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service"; -import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; -import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; @Controller('shift') diff --git a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto.ts b/src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto.ts rename to src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto.ts b/src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto.ts rename to src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts b/src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto.ts similarity index 76% rename from src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts rename to src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto.ts index ce16f8e..0dd4505 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto.ts +++ b/src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto.ts @@ -1,6 +1,6 @@ import { PartialType, OmitType } from "@nestjs/swagger"; -import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; import { IsInt } from "class-validator"; +import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; export class UpdateShiftDto extends PartialType( // allows update using ShiftDto and preventing OmitType variables to be modified diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-archival.service.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/shifts/services/shifts-archival.service.ts rename to src/time-and-attendance/time-tracker/shifts/services/shifts-archival.service.ts diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts similarity index 95% rename from src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts rename to src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts index c1c2e12..d1eed96 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-get.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-get.service.ts @@ -2,7 +2,7 @@ import { toStringFromDate, toStringFromHHmm } from "src/time-and-attendance/util import { Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { shift_select } from "src/time-and-attendance/utils/selects.utils"; -import { GetShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto"; +import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; /** diff --git a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts similarity index 97% rename from src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts rename to src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 5a1e6c4..44261cb 100644 --- a/src/time-and-attendance/modules/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -2,12 +2,12 @@ import { CreateShiftResult, NormedOk, NormedErr, UpdateShiftResult, UpdateShiftP import { overlaps, toStringFromHHmm, toStringFromDate, toDateFromString, toHHmmFromString, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils"; import { Injectable, BadRequestException, ConflictException, NotFoundException } from "@nestjs/common"; import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; -import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; import { PrismaService } from "src/prisma/prisma.service"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; +import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; +import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; import { shift_select } from "src/time-and-attendance/utils/selects.utils"; -import { GetShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto"; -import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; diff --git a/src/time-and-attendance/time-tracker/shifts/shifts.module.ts b/src/time-and-attendance/time-tracker/shifts/shifts.module.ts new file mode 100644 index 0000000..3dc407d --- /dev/null +++ b/src/time-and-attendance/time-tracker/shifts/shifts.module.ts @@ -0,0 +1,14 @@ + +import { BusinessLogicsModule } from 'src/time-and-attendance/domains/business-logics.module'; +import { Module } from '@nestjs/common'; +import { ShiftController } from 'src/time-and-attendance/time-tracker/shifts/controllers/shift.controller'; +import { ShiftsGetService } from 'src/time-and-attendance/time-tracker/shifts/services/shifts-get.service'; +import { ShiftsUpsertService } from 'src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service'; + +@Module({ + imports: [ BusinessLogicsModule ], + controllers: [ShiftController], + providers: [ ShiftsGetService, ShiftsUpsertService ], + exports: [ ShiftsUpsertService ], +}) +export class ShiftsModule {} diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts similarity index 90% rename from src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts rename to src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts index 414930a..90cf522 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts @@ -1,5 +1,5 @@ import { Controller, Get, ParseIntPipe, Query, Req, UnauthorizedException} from "@nestjs/common"; -import { GetTimesheetsOverviewService } from "src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service"; +import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service"; @Controller('timesheets') export class TimesheetController { diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/dtos/timesheet.dto.ts b/src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/dtos/timesheet.dto.ts rename to src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-approval.service.ts b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-approval.service.ts rename to src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service.ts b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service.ts similarity index 100% rename from src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service.ts rename to src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service.ts diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts similarity index 99% rename from src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts rename to src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts index c6da872..6ea81e2 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -3,7 +3,7 @@ import { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/time-and-attendance/utils/co import { Injectable, NotFoundException } from "@nestjs/common"; import { TotalExpenses, TotalHours } from "src/time-and-attendance/utils/type.utils"; import { PrismaService } from "src/prisma/prisma.service"; -import { EmailToIdResolver } from "src/time-and-attendance/modules/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; @Injectable() export class GetTimesheetsOverviewService { diff --git a/src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts similarity index 50% rename from src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts rename to src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts index 595d660..f559f6d 100644 --- a/src/time-and-attendance/modules/time-tracker/timesheets/timesheets.module.ts +++ b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts @@ -1,8 +1,9 @@ -import { GetTimesheetsOverviewService } from 'src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-get-overview.service'; -import { TimesheetArchiveService } from 'src/time-and-attendance/modules/time-tracker/timesheets/services/timesheet-archive.service'; -import { TimesheetController } from 'src/time-and-attendance/modules/time-tracker/timesheets/controllers/timesheet.controller'; -import { SharedModule } from 'src/time-and-attendance/modules/shared/shared.module'; + import { Module } from '@nestjs/common'; +import { SharedModule } from 'src/time-and-attendance/shared/shared.module'; +import { TimesheetController } from 'src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller'; +import { TimesheetArchiveService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service'; +import { GetTimesheetsOverviewService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service'; @Module({ imports: [SharedModule], diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts index 0264f3f..5e846c1 100644 --- a/src/time-and-attendance/utils/type.utils.ts +++ b/src/time-and-attendance/utils/type.utils.ts @@ -1,12 +1,13 @@ -import { WeekOvertimeSummary } from "src/time-and-attendance/domains/services/overtime.service"; -import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; -import { UpdateShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-update.dto"; -import { GetShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-get.dto"; -import { ShiftDto } from "src/time-and-attendance/modules/time-tracker/shifts/dtos/shift-create.dto"; import { Prisma } from "@prisma/client"; -import { SchedulePresetsDto } from "src/time-and-attendance/modules/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; -import { GetExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-get.dto"; -import { updateExpenseDto } from "src/time-and-attendance/modules/expenses/dtos/expense-update.dto"; +import { WeekOvertimeSummary } from "src/time-and-attendance/domains/services/overtime.service"; +import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto"; +import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto"; +import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; +import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; +import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; +import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; +import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; + export type TotalHours = { regular: number; From c59b50a8291199373e6f788a8d9af71493f7d94d Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 3 Nov 2025 11:19:21 -0500 Subject: [PATCH 08/12] refactor(schedule-presets): modified route and params to use session data --- docs/swagger/swagger-spec.json | 36 +++---------------- .../controller/schedule-presets.controller.ts | 33 +++++++++-------- .../schedule-presets-apply.service.ts | 11 +++--- .../services/schedule-presets-get.service.ts | 9 +++-- .../schedule-presets-upsert.service.ts | 8 +++-- 5 files changed, 39 insertions(+), 58 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index af88b3e..cca65b6 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -446,19 +446,10 @@ ] } }, - "/schedule-presets/create/{employee_id}": { + "/schedule-presets/create": { "post": { "operationId": "SchedulePresetsController_createPreset", - "parameters": [ - { - "name": "employee_id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], + "parameters": [], "requestBody": { "required": true, "content": { @@ -535,19 +526,10 @@ ] } }, - "/schedule-presets/find/{employee_id}": { + "/schedule-presets/find-list": { "get": { "operationId": "SchedulePresetsController_findListById", - "parameters": [ - { - "name": "employee_id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], + "parameters": [], "responses": { "200": { "description": "" @@ -558,18 +540,10 @@ ] } }, - "/schedule-presets/apply-presets/{employee_id}": { + "/schedule-presets/apply-presets": { "post": { "operationId": "SchedulePresetsController_applyPresets", "parameters": [ - { - "name": "employee_id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - }, { "name": "preset", "required": true, diff --git a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index 3a2c692..56fc28a 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch } from "@nestjs/common"; +import { Controller, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch, Req } from "@nestjs/common"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; import { SchedulePresetsUpdateDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto"; import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; @@ -9,18 +9,18 @@ import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-track @Controller('schedule-presets') export class SchedulePresetsController { constructor( - private readonly upsertService: SchedulePresetsUpsertService, + private readonly upsertService: SchedulePresetsUpsertService, + private readonly getService: SchedulePresetsGetService, private readonly applyPresetsService: SchedulePresetsApplyService, - private readonly getService: SchedulePresetsGetService, ){} //used to create a schedule preset - @Post('create/:employee_id') - async createPreset( - @Param('employee_id', ParseIntPipe) employee_id: number, + @Post('create') + async createPreset( @Req() req, @Body() dto: SchedulePresetsDto, ) { - return await this.upsertService.createPreset(employee_id, dto); + const email = req.user?.email; + return await this.upsertService.createPreset(email, dto); } //used to update an already existing schedule preset @@ -42,23 +42,22 @@ export class SchedulePresetsController { //used to show the list of available schedule presets - @Get('find/:employee_id') - async findListById( - @Param('employee_id', ParseIntPipe) employee_id: number, - ) { - return this.getService.getSchedulePresets(employee_id); + @Get('find-list') + async findListById( @Req() req) { + const email = req.user?.email; + return this.getService.getSchedulePresets(email); } //used to apply a preset to a timesheet - @Post('/apply-presets/:employee_id') - async applyPresets( - @Param('employee_id') employee_id: number, + @Post('apply-presets') + async applyPresets( @Req() req, @Query('preset') preset_name: string, - @Query('start') start_date: string, + @Query('start') start_date: string, ) { + const email = req.user?.email; if(!preset_name?.trim()) throw new BadRequestException('Query "preset" is required'); if(!start_date?.trim()) throw new BadRequestException('Query "start" is required YYYY-MM-DD'); - return this.applyPresetsService.applyToTimesheet(employee_id, preset_name, start_date); + return this.applyPresetsService.applyToTimesheet(email, preset_name, start_date); } } \ No newline at end of file diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts index 75bdf1c..9815e10 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts @@ -4,20 +4,19 @@ import { DATE_ISO_FORMAT } from "src/time-and-attendance/utils/constants.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { ApplyResult } from "src/time-and-attendance/utils/type.utils"; import { WEEKDAY } from "src/time-and-attendance/utils/mappers.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; @Injectable() export class SchedulePresetsApplyService { - constructor( private readonly prisma: PrismaService) {} + constructor( private readonly prisma: PrismaService, private readonly emailResolver: EmailToIdResolver) {} - async applyToTimesheet( - employee_id: number, - preset_name: string, - start_date_iso: string, - ): Promise { + async applyToTimesheet( email: string, preset_name: string, start_date_iso: string ): Promise { if(!preset_name?.trim()) throw new BadRequestException('A preset_name is required'); if(!DATE_ISO_FORMAT.test(start_date_iso)) throw new BadRequestException('start_date must be of format :YYYY-MM-DD'); + const employee_id = await this.emailResolver.findIdByEmail(email); + const preset = await this.prisma.schedulePresets.findFirst({ where: { employee_id, name: preset_name }, include: { diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts index e73227d..8b89c80 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts @@ -2,13 +2,18 @@ import { PresetResponse, ShiftResponse } from "src/time-and-attendance/utils/typ import { PrismaService } from "src/prisma/prisma.service"; import { Injectable } from "@nestjs/common"; import { Prisma } from "@prisma/client"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; @Injectable() export class SchedulePresetsGetService { - constructor( private readonly prisma: PrismaService ){} + constructor( + private readonly prisma: PrismaService, + private readonly emailResolver: EmailToIdResolver, + ){} - async getSchedulePresets(employee_id: number): Promise { + async getSchedulePresets(email: string): Promise { try { + const employee_id = await this.emailResolver.findIdByEmail(email); const presets = await this.prisma.schedulePresets.findMany({ where: { employee_id }, orderBy: [{is_default: 'desc' }, { name: 'asc' }], diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts index ec60558..534082e 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts @@ -5,20 +5,24 @@ import { toHHmmFromDate } from "src/time-and-attendance/utils/date-time.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; +import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; @Injectable() export class SchedulePresetsUpsertService { constructor( private readonly prisma: PrismaService, private readonly typeResolver : BankCodesResolver, + private readonly emailResolver: EmailToIdResolver, ){} //_________________________________________________________________ // CREATE //_________________________________________________________________ - async createPreset( employee_id: number, dto: SchedulePresetsDto): Promise { + async createPreset( email: string, dto: SchedulePresetsDto): Promise { try { const shifts_data = await this.resolveAndBuildPresetShifts(dto); - if(!shifts_data) throw new BadRequestException(`Employee with id: ${employee_id} or dto not found`); + const employee_id = await this.emailResolver.findIdByEmail(email); + if(!shifts_data) throw new BadRequestException(`Employee with email: ${email} or dto not found`); + await this.prisma.$transaction(async (tx)=> { if(dto.is_default) { await tx.schedulePresets.updateMany({ From f1f765b35051d41e052397dc001a7837b2e4e98c Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 3 Nov 2025 11:47:41 -0500 Subject: [PATCH 09/12] clean(folder): cleaning imports --- docs/swagger/swagger-spec.json | 265 +----------------- src/modules/exports/csv-exports.module.ts | 3 +- .../domains/business-logics.module.ts | 5 +- .../domains/services/holiday.service.ts | 30 +- .../domains/services/mileage.service.ts | 2 +- .../domains/services/overtime.service.ts | 171 +---------- .../domains/services/sick-leave.service.ts | 2 +- .../domains/services/vacation.service.ts | 2 +- .../services/expense-upsert.service.ts | 2 +- .../leave-requests/leave-requests.module.ts | 4 +- .../holiday-leave-requests.service.ts | 6 +- .../services/leave-request.service.ts | 6 +- .../services/sick-leave-requests.service.ts | 6 +- .../vacation-leave-requests.service.ts | 6 +- .../controllers/pay-periods.controller.ts | 43 +-- .../pay-period/dtos/bulk-crew-approval.dto.ts | 2 +- .../pay-period/dtos/bundle-pay-period.dto.ts | 5 - .../dtos/overview-employee-period.dto.ts | 31 -- .../dtos/overview-pay-period.dto.ts | 35 --- .../pay-period/dtos/pay-period.dto.ts | 17 -- .../shared/helpers/date-time.helpers.ts | 34 --- .../shared/interfaces/shifts.interface.ts | 9 - .../shared/selects/expenses.select.ts | 11 - .../shared/selects/pay-periods.select.ts | 4 - .../shared/selects/shifts.select.ts | 12 - .../shared/shared.module.ts | 22 -- .../time-and-attendance.module.ts | 16 +- .../schedule-presets.module.ts | 2 - .../schedule-presets-apply.service.ts | 2 +- .../services/schedule-presets-get.service.ts | 2 +- .../schedule-presets-upsert.service.ts | 4 +- .../shifts/services/shifts-upsert.service.ts | 2 +- .../timesheet-get-overview.service.ts | 2 +- .../timesheets/timesheets.module.ts | 2 - .../utils/constants.utils.ts | 2 + .../utils/date-time.utils.ts | 37 ++- .../utils/resolve-bank-type-id.utils.ts | 0 .../utils/resolve-email-id.utils.ts | 0 .../utils/resolve-full-name.utils.ts | 0 .../utils/resolve-shifts-id.utils.ts | 2 +- .../utils/resolve-timesheet.utils.ts | 0 .../utils/selects.utils.ts | 31 ++ src/time-and-attendance/utils/type.utils.ts | 33 ++- 43 files changed, 176 insertions(+), 696 deletions(-) delete mode 100644 src/time-and-attendance/shared/helpers/date-time.helpers.ts delete mode 100644 src/time-and-attendance/shared/interfaces/shifts.interface.ts delete mode 100644 src/time-and-attendance/shared/selects/expenses.select.ts delete mode 100644 src/time-and-attendance/shared/selects/pay-periods.select.ts delete mode 100644 src/time-and-attendance/shared/selects/shifts.select.ts delete mode 100644 src/time-and-attendance/shared/shared.module.ts rename src/time-and-attendance/{shared => }/utils/resolve-bank-type-id.utils.ts (100%) rename src/time-and-attendance/{shared => }/utils/resolve-email-id.utils.ts (100%) rename src/time-and-attendance/{shared => }/utils/resolve-full-name.utils.ts (100%) rename src/time-and-attendance/{shared => }/utils/resolve-shifts-id.utils.ts (93%) rename src/time-and-attendance/{shared => }/utils/resolve-timesheet.utils.ts (100%) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index cca65b6..8aa7528 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -91,30 +91,20 @@ "parameters": [ { "name": "date", - "required": false, + "required": true, "in": "query", - "description": "Override for resolving the current period", "schema": { - "example": "2025-08-11", "type": "string" } } ], "responses": { "200": { - "description": "Find current and all pay periods", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PayPeriodBundleDto" - } - } - } + "description": "" } }, - "summary": "Return current pay period and the full list", "tags": [ - "pay-periods" + "PayPeriods" ] } }, @@ -133,22 +123,11 @@ ], "responses": { "200": { - "description": "Pay period found for the selected date", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PayPeriodDto" - } - } - } - }, - "404": { - "description": "Pay period not found for the selected date" + "description": "" } }, - "summary": "Resolve a period by a date within it", "tags": [ - "pay-periods" + "PayPeriods" ] } }, @@ -161,7 +140,6 @@ "required": true, "in": "path", "schema": { - "example": 2024, "type": "number" } }, @@ -169,31 +147,18 @@ "name": "periodNumber", "required": true, "in": "path", - "description": "1..26", "schema": { - "example": 1, "type": "number" } } ], "responses": { "200": { - "description": "Pay period found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PayPeriodDto" - } - } - } - }, - "404": { - "description": "Pay period not found" + "description": "" } }, - "summary": "Find pay period by year and period number", "tags": [ - "pay-periods" + "PayPeriods" ] } }, @@ -206,7 +171,6 @@ "required": true, "in": "path", "schema": { - "example": 2024, "type": "number" } }, @@ -214,49 +178,18 @@ "name": "periodNumber", "required": true, "in": "path", - "description": "1..26", "schema": { - "example": 1, "type": "number" } - }, - { - "name": "email", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - }, - { - "name": "includeSubtree", - "required": false, - "in": "query", - "description": "Include indirect reports", - "schema": { - "example": false, - "type": "boolean" - } } ], "responses": { "200": { - "description": "Crew overview", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PayPeriodOverviewDto" - } - } - } - }, - "404": { - "description": "Pay period not found" + "description": "" } }, - "summary": "Supervisor crew overview for a given pay period", "tags": [ - "pay-periods" + "PayPeriods" ] } }, @@ -269,7 +202,6 @@ "required": true, "in": "path", "schema": { - "example": 2024, "type": "number" } }, @@ -277,31 +209,18 @@ "name": "periodNumber", "required": true, "in": "path", - "description": "1..26", "schema": { - "example": 1, "type": "number" } } ], "responses": { "200": { - "description": "Pay period overview found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PayPeriodOverviewDto" - } - } - } - }, - "404": { - "description": "Pay period not found" + "description": "" } }, - "summary": "Detailed view of a pay period by year + number", "tags": [ - "pay-periods" + "PayPeriods" ] } }, @@ -690,168 +609,6 @@ } }, "schemas": { - "PayPeriodDto": { - "type": "object", - "properties": { - "pay_period_no": { - "type": "number", - "example": 1, - "description": "numéro cyclique de la période entre 1 et 26" - }, - "period_start": { - "type": "string", - "example": "2023-12-17", - "format": "date" - }, - "period_end": { - "type": "string", - "example": "2023-12-30", - "format": "date" - }, - "payday": { - "type": "string", - "example": "2023-01-04", - "format": "date" - }, - "pay_year": { - "type": "number", - "example": 2023 - }, - "label": { - "type": "string", - "example": "2023-12-17 → 2023-12-30" - } - }, - "required": [ - "pay_period_no", - "period_start", - "period_end", - "payday", - "pay_year", - "label" - ] - }, - "PayPeriodBundleDto": { - "type": "object", - "properties": { - "current": { - "description": "Current pay period (resolved from date)", - "allOf": [ - { - "$ref": "#/components/schemas/PayPeriodDto" - } - ] - }, - "periods": { - "description": "All pay periods", - "type": "array", - "items": { - "$ref": "#/components/schemas/PayPeriodDto" - } - } - }, - "required": [ - "current", - "periods" - ] - }, - "EmployeePeriodOverviewDto": { - "type": "object", - "properties": { - "employee_name": { - "type": "string", - "example": "Alex Dupont", - "description": "Nom complet de lemployé" - }, - "regular_hours": { - "type": "number", - "example": 40, - "description": "pay-period`s regular hours" - }, - "other_hours": { - "type": "object", - "example": 0, - "description": "pay-period`s other hours" - }, - "expenses": { - "type": "number", - "example": 420.69, - "description": "pay-period`s total expenses ($)" - }, - "mileage": { - "type": "number", - "example": 40, - "description": "pay-period total mileages (km)" - }, - "is_approved": { - "type": "boolean", - "example": true, - "description": "Tous les timesheets de la période sont approuvés pour cet employé" - } - }, - "required": [ - "employee_name", - "regular_hours", - "other_hours", - "expenses", - "mileage", - "is_approved" - ] - }, - "PayPeriodOverviewDto": { - "type": "object", - "properties": { - "pay_period_no": { - "type": "number", - "example": 1, - "description": "Period number (1–26)" - }, - "pay_year": { - "type": "number", - "example": 2023, - "description": "Calendar year of the period" - }, - "period_start": { - "type": "string", - "example": "2023-12-17", - "format": "date", - "description": "Period start date (YYYY-MM-DD)" - }, - "period_end": { - "type": "string", - "example": "2023-12-30", - "format": "date", - "description": "Period end date (YYYY-MM-DD)" - }, - "payday": { - "type": "string", - "example": "2023-12-30", - "format": "date", - "description": "Period pay day(YYYY-MM-DD)" - }, - "label": { - "type": "string", - "example": "2023-12-17 → 2023-12-30", - "description": "Human-readable label" - }, - "employees_overview": { - "description": "Per-employee overview for the period", - "type": "array", - "items": { - "$ref": "#/components/schemas/EmployeePeriodOverviewDto" - } - } - }, - "required": [ - "pay_period_no", - "pay_year", - "period_start", - "period_end", - "payday", - "label", - "employees_overview" - ] - }, "PreferencesDto": { "type": "object", "properties": {} diff --git a/src/modules/exports/csv-exports.module.ts b/src/modules/exports/csv-exports.module.ts index 30a9b8d..92a5a96 100644 --- a/src/modules/exports/csv-exports.module.ts +++ b/src/modules/exports/csv-exports.module.ts @@ -1,10 +1,9 @@ import { Module } from "@nestjs/common"; import { CsvExportController } from "./controllers/csv-exports.controller"; import { CsvExportService } from "./services/csv-exports.service"; -import { SharedModule } from "src/time-and-attendance/shared/shared.module"; @Module({ - providers:[CsvExportService, SharedModule], + providers:[CsvExportService], controllers: [CsvExportController], }) export class CsvExportModule {} diff --git a/src/time-and-attendance/domains/business-logics.module.ts b/src/time-and-attendance/domains/business-logics.module.ts index 91774ec..4dc801e 100644 --- a/src/time-and-attendance/domains/business-logics.module.ts +++ b/src/time-and-attendance/domains/business-logics.module.ts @@ -4,15 +4,18 @@ import { VacationService } from "./services/vacation.service"; import { HolidayService } from "./services/holiday.service"; import { MileageService } from "./services/mileage.service"; import { Module } from "@nestjs/common"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @Module({ + imports:[], providers: [ HolidayService, MileageService, OvertimeService, SickLeaveService, - VacationService + VacationService, + EmailToIdResolver, ], exports: [ HolidayService, diff --git a/src/time-and-attendance/domains/services/holiday.service.ts b/src/time-and-attendance/domains/services/holiday.service.ts index 15bf4d2..5a0c2b7 100644 --- a/src/time-and-attendance/domains/services/holiday.service.ts +++ b/src/time-and-attendance/domains/services/holiday.service.ts @@ -1,9 +1,8 @@ import { Injectable, Logger, NotFoundException } from "@nestjs/common"; import { computeHours, getWeekStart } from "src/common/utils/date-utils"; -import { PrismaService } from "../../../prisma/prisma.service"; - -const WEEK_IN_MS = 7 * 24 * 60 * 60 * 1000; - +import { PrismaService } from "src/prisma/prisma.service"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; +import { MS_PER_WEEK } from "src/time-and-attendance/utils/constants.utils"; /* le calcul est 1/20 des 4 dernières semaines, précédent la semaine incluant le férier. Un maximum de 08h00 est allouable pour le férier @@ -15,28 +14,19 @@ const WEEK_IN_MS = 7 * 24 * 60 * 60 * 1000; export class HolidayService { private readonly logger = new Logger(HolidayService.name); - constructor(private readonly prisma: PrismaService) {} - - //fetch employee_id by email - private async resolveEmployeeByEmail(email: string): Promise { - const employee = await this.prisma.employees.findFirst({ - where: { - user: { email } - }, - select: { id: true }, - }); - if(!employee) throw new NotFoundException(`Employee with email : ${email} not found`); - return employee.id; - } + constructor( + private readonly prisma: PrismaService, + private readonly emailResolver: EmailToIdResolver, + ) {} private async computeHoursPrevious4WeeksByEmail(email: string, holiday_date: Date): Promise { - const employee_id = await this.resolveEmployeeByEmail(email); + const employee_id = await this.emailResolver.findIdByEmail(email); return this.computeHoursPrevious4Weeks(employee_id, holiday_date); } private async computeHoursPrevious4Weeks(employee_id: number, holiday_date: Date): Promise { const holiday_week_start = getWeekStart(holiday_date); - const window_start = new Date(holiday_week_start.getTime() - 4 * WEEK_IN_MS); + const window_start = new Date(holiday_week_start.getTime() - 4 * MS_PER_WEEK); const window_end = new Date(holiday_week_start.getTime() - 1); const valid_codes = ['G1', 'G43', 'G56', 'G104', 'G105', 'G700']; @@ -60,7 +50,7 @@ export class HolidayService { let capped_total = 0; for(let offset = 1; offset <= 4; offset++) { - const week_start = new Date(holiday_week_start.getTime() - offset * WEEK_IN_MS); + const week_start = new Date(holiday_week_start.getTime() - offset * MS_PER_WEEK); const key = week_start.getTime(); const weekly_hours = hours_by_week.get(key) ?? 0; capped_total += Math.min(weekly_hours, 40); diff --git a/src/time-and-attendance/domains/services/mileage.service.ts b/src/time-and-attendance/domains/services/mileage.service.ts index 030ce42..2b589a9 100644 --- a/src/time-and-attendance/domains/services/mileage.service.ts +++ b/src/time-and-attendance/domains/services/mileage.service.ts @@ -1,5 +1,5 @@ import { BadRequestException, Injectable, Logger } from "@nestjs/common"; -import { PrismaService } from '../../../prisma/prisma.service'; +import { PrismaService } from "src/prisma/prisma.service"; @Injectable() export class MileageService { diff --git a/src/time-and-attendance/domains/services/overtime.service.ts b/src/time-and-attendance/domains/services/overtime.service.ts index be8c15c..e43c4d3 100644 --- a/src/time-and-attendance/domains/services/overtime.service.ts +++ b/src/time-and-attendance/domains/services/overtime.service.ts @@ -1,32 +1,15 @@ import { Injectable, Logger } from '@nestjs/common'; -import { PrismaService } from '../../../prisma/prisma.service'; import { getWeekStart, getWeekEnd, computeHours } from 'src/common/utils/date-utils'; -import { Prisma, PrismaClient } from '@prisma/client'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { DAILY_LIMIT_HOURS, WEEKLY_LIMIT_HOURS } from 'src/time-and-attendance/utils/constants.utils'; +import { Tx, WeekOvertimeSummary } from 'src/time-and-attendance/utils/type.utils'; -type Tx = Prisma.TransactionClient | PrismaClient; - -export type WeekOvertimeSummary = { - week_start:string; - week_end: string; - week_total_hours: number; - weekly_overtime: number; - daily_overtime_kept: number; - total_overtime: number; - breakdown: Array<{ - date:string; - day_hours: number; - day_overtime: number; - daily_kept: number; - running_total_before: number; - }>; -}; @Injectable() export class OvertimeService { private logger = new Logger(OvertimeService.name); - private daily_max = 8; // maximum for regular hours per day - private weekly_max = 40; // maximum for regular hours per week + private INCLUDED_TYPES = ['EMERGENCY', 'EVENING','OVERTIME','REGULAR'] as const; // included types for weekly overtime calculation constructor(private prisma: PrismaService) {} @@ -61,7 +44,7 @@ export class OvertimeService { } const week_total_hours = [ ...day_totals.values()].reduce((a,b) => a + b, 0); - const weekly_overtime = Math.max(0, week_total_hours - this.weekly_max); + const weekly_overtime = Math.max(0, week_total_hours - WEEKLY_LIMIT_HOURS); let running = 0; let daily_kept_sum = 0; @@ -69,9 +52,9 @@ export class OvertimeService { for (const key of days) { const day_hours = day_totals.get(key) ?? 0; - const day_overtime = Math.max(0, day_hours - this.daily_max); + const day_overtime = Math.max(0, day_hours - DAILY_LIMIT_HOURS); - const cap_before_40 = Math.max(0, this.weekly_max - running); + const cap_before_40 = Math.max(0, WEEKLY_LIMIT_HOURS - running); const daily_kept = Math.min(day_overtime, cap_before_40); breakdown.push({ @@ -104,144 +87,4 @@ export class OvertimeService { breakdown, }; } - - // //calculate daily overtime - // async getDailyOvertimeHours(timesheet_id: number, date: Date): Promise { - // const shifts = await this.prisma.shifts.findMany({ - // where: { - // timesheet_id, - // date: date, - // bank_code: { type: { in: this.INCLUDED_TYPES as unknown as string[] } }, - // }, - // select: { start_time: true, end_time: true }, - // orderBy: [{ start_time: 'asc' }], - // }); - - // const total = shifts.map((shift)=> - // computeHours(shift.start_time, shift.end_time, 5)). - // reduce((sum, hours)=> sum + hours, 0); - - // const overtime = Math.max(0, total - this.daily_max); - - // this.logger.debug(`[OVERTIME]-[DAILY] total=${total.toFixed(2)}h, overtime= ${overtime.toFixed(2)}h`); - // return overtime; - // } - - // //calculate Weekly overtime - // async getWeeklyOvertimeHours(timesheet_id: number, ref_date: Date): Promise { - // const week_start = getWeekStart(ref_date); - // const week_end = getWeekEnd(week_start); - - // //fetches all shifts from INCLUDED_TYPES array - // const included_shifts = await this.prisma.shifts.findMany({ - // where: { - // timesheet_id, - // date: { gte:week_start, lte: week_end }, - // bank_code: { type: { in: this.INCLUDED_TYPES as unknown as string[] } }, - // }, - // select: { start_time: true, end_time: true }, - // orderBy: [{date: 'asc'}, {start_time:'asc'}], - // }); - - // //calculate total hours of those shifts minus weekly Max to find total overtime hours - // const total = included_shifts.map(shift => - // computeHours(shift.start_time, shift.end_time, 5)). - // reduce((sum, hours)=> sum+hours, 0); - - // const overtime = Math.max(0, total - this.weekly_max); - - // this.logger.debug(`[OVERTIME]-[WEEKLY] total=${total.toFixed(2)}h, overtime= ${overtime.toFixed(2)}h`); - // return overtime; - // } - - - // //transform REGULAR shifts to OVERTIME when exceed 40hrs of included_types of shift - // async transformRegularHoursToWeeklyOvertime( - // employee_id: number, - // ref_date: Date, - // tx?: Prisma.TransactionClient, - // ): Promise { - // //ensures the use of the transaction if needed. fallback to this.prisma if no transaction is detected. - // const db = tx ?? this.prisma; - - // //calculate weekly overtime - // const overtime_hours = await this.getWeeklyOvertimeHours(employee_id, ref_date); - // if(overtime_hours <= 0) return; - - // const convert_to_minutes = Math.round(overtime_hours * 60); - - // const [regular, overtime] = await Promise.all([ - // db.bankCodes.findFirst({where: { type: 'REGULAR' }, select: { id: true } }), - // db.bankCodes.findFirst({where: { type: 'OVERTIME'}, select: { id: true } }), - // ]); - // if(!regular || !overtime) return; - - // const week_start = getWeekStart(ref_date); - // const week_end = getWeekEnd(week_start); - - // //gets all regular shifts and order them by desc - // const regular_shifts_desc = await db.shifts.findMany({ - // where: { - // date: { gte:week_start, lte: week_end }, - // timesheet: { employee_id }, - // bank_code_id: regular.id, - // }, - // select: { - // id: true, - // timesheet_id: true, - // date: true, - // start_time: true, - // end_time: true, - // is_remote: true, - // comment: true, - // }, - // orderBy: [{date: 'desc'}, {start_time:'desc'}], - // }); - - // let remaining_minutes = convert_to_minutes; - - // for(const shift of regular_shifts_desc) { - // if(remaining_minutes <= 0) break; - - // const start = shift.start_time; - // const end = shift.end_time; - // const duration_in_minutes = Math.max(0, Math.round((end.getTime() - start.getTime())/60000)); - // if(duration_in_minutes === 0) continue; - - // if(duration_in_minutes <= remaining_minutes) { - // await db.shifts.update({ - // where: { id: shift.id }, - // data: { bank_code_id: overtime.id }, - // }); - // remaining_minutes -= duration_in_minutes; - // continue; - // } - // //sets the start_time of the new overtime shift - // const new_overtime_start = new Date(end.getTime() - remaining_minutes * 60000); - - // //shorten the regular shift - // await db.shifts.update({ - // where: { id: shift.id }, - // data: { end_time: new_overtime_start }, - // }); - - // //creates the new overtime shift to replace the shorten regular shift - // await db.shifts.create({ - // data: { - // timesheet_id: shift.timesheet_id, - // date: shift.date, - // start_time: new_overtime_start, - // end_time: end, - // is_remote: shift.is_remote, - // comment: shift.comment, - // bank_code_id: overtime.id, - // }, - // }); - // remaining_minutes = 0; - // } - // this.logger.debug(`[OVERTIME]-[WEEKLY]-[TRANSFORM] emp=${employee_id} - // week: ${week_start.toISOString().slice(0,10)}..${week_end.toISOString().slice(0,10)} - // converted= ${(convert_to_minutes-remaining_minutes)/60}h`); - // } - } diff --git a/src/time-and-attendance/domains/services/sick-leave.service.ts b/src/time-and-attendance/domains/services/sick-leave.service.ts index 6c00113..60a847e 100644 --- a/src/time-and-attendance/domains/services/sick-leave.service.ts +++ b/src/time-and-attendance/domains/services/sick-leave.service.ts @@ -1,6 +1,6 @@ import { getYearStart, roundToQuarterHour } from "src/common/utils/date-utils"; import { Injectable, Logger } from "@nestjs/common"; -import { PrismaService } from "../../../prisma/prisma.service"; +import { PrismaService } from "src/prisma/prisma.service"; @Injectable() export class SickLeaveService { diff --git a/src/time-and-attendance/domains/services/vacation.service.ts b/src/time-and-attendance/domains/services/vacation.service.ts index 9445149..2e3a214 100644 --- a/src/time-and-attendance/domains/services/vacation.service.ts +++ b/src/time-and-attendance/domains/services/vacation.service.ts @@ -1,5 +1,5 @@ import { Injectable, Logger, NotFoundException } from "@nestjs/common"; -import { PrismaService } from "../../../prisma/prisma.service"; +import { PrismaService } from "src/prisma/prisma.service"; @Injectable() export class VacationService { diff --git a/src/time-and-attendance/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/expenses/services/expense-upsert.service.ts index 86a1c9b..d48ea50 100644 --- a/src/time-and-attendance/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/expenses/services/expense-upsert.service.ts @@ -6,7 +6,7 @@ import { expense_select } from "src/time-and-attendance/utils/selects.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @Injectable() diff --git a/src/time-and-attendance/leave-requests/leave-requests.module.ts b/src/time-and-attendance/leave-requests/leave-requests.module.ts index a822a27..eff4f6d 100644 --- a/src/time-and-attendance/leave-requests/leave-requests.module.ts +++ b/src/time-and-attendance/leave-requests/leave-requests.module.ts @@ -1,15 +1,13 @@ import { LeaveRequestController } from "src/time-and-attendance/leave-requests/controllers/leave-requests.controller"; import { LeaveRequestsService } from "src/time-and-attendance/leave-requests/services/leave-request.service"; import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module"; -import { SharedModule } from "src/time-and-attendance/shared/shared.module"; import { ShiftsModule } from "src/time-and-attendance/time-tracker/shifts/shifts.module"; import { Module } from "@nestjs/common"; @Module({ imports: [ BusinessLogicsModule, - ShiftsModule, - SharedModule + ShiftsModule, ], controllers: [LeaveRequestController], providers: [LeaveRequestsService], diff --git a/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts index 5991686..ce4807b 100644 --- a/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/holiday-leave-requests.service.ts @@ -1,15 +1,15 @@ import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto"; import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; -import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; import { LeaveRequestsUtils } from "src/time-and-attendance/leave-requests/utils/leave-request.util"; -import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; import { PrismaService } from "src/prisma/prisma.service"; import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/utils/date-time.utils"; diff --git a/src/time-and-attendance/leave-requests/services/leave-request.service.ts b/src/time-and-attendance/leave-requests/services/leave-request.service.ts index 6390a10..499c2d8 100644 --- a/src/time-and-attendance/leave-requests/services/leave-request.service.ts +++ b/src/time-and-attendance/leave-requests/services/leave-request.service.ts @@ -1,18 +1,18 @@ import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; -import { normalizeDates, toDateOnly, toISODateKey } from "src/time-and-attendance/shared/helpers/date-time.helpers"; import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto"; import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto"; import { roundToQuarterHour } from "src/common/utils/date-utils"; import { LeaveRequestsUtils } from "src/time-and-attendance/leave-requests/utils/leave-request.util"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; -import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; +import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; import { PrismaService } from "src/prisma/prisma.service"; import { mapRowToView } from "../mappers/leave-requests.mapper"; +import { normalizeDates, toDateOnly, toISODateKey } from "src/time-and-attendance/utils/date-time.utils"; @Injectable() export class LeaveRequestsService { constructor( diff --git a/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts index 629dbff..1cff7a8 100644 --- a/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/sick-leave-requests.service.ts @@ -1,15 +1,15 @@ import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto"; import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; -import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers"; import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; import { roundToQuarterHour } from "src/common/utils/date-utils"; -import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service"; import { PrismaService } from "src/prisma/prisma.service"; import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/utils/date-time.utils"; diff --git a/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts b/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts index d6f16a5..2e71c39 100644 --- a/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts +++ b/src/time-and-attendance/leave-requests/services/vacation-leave-requests.service.ts @@ -1,15 +1,15 @@ import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common"; import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto"; import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client"; -import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers"; import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils"; import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto"; import { roundToQuarterHour } from "src/common/utils/date-utils"; -import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; import { VacationService } from "src/time-and-attendance/domains/services/vacation.service"; import { PrismaService } from "src/prisma/prisma.service"; import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper"; +import { normalizeDates, toDateOnly } from "src/time-and-attendance/utils/date-time.utils"; @Injectable() diff --git a/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts b/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts index 61026c6..65ed977 100644 --- a/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts +++ b/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts @@ -1,5 +1,4 @@ -import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query } from "@nestjs/common"; -import { ApiNotFoundResponse, ApiOperation, ApiParam, ApiQuery, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Query, Req } from "@nestjs/common"; import { PayPeriodDto } from "../dtos/pay-period.dto"; import { PayPeriodOverviewDto } from "../dtos/overview-pay-period.dto"; import { PayPeriodsQueryService } from "../services/pay-periods-query.service"; @@ -9,7 +8,7 @@ import { Roles as RoleEnum } from '.prisma/client'; import { PayPeriodBundleDto } from "../dtos/bundle-pay-period.dto"; import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto"; -@ApiTags('pay-periods') + @Controller('pay-periods') export class PayPeriodsController { @@ -19,9 +18,6 @@ export class PayPeriodsController { ) {} @Get('current-and-all') - @ApiOperation({summary: 'Return current pay period and the full list'}) - @ApiQuery({name: 'date', required:false, example: '2025-08-11', description:'Override for resolving the current period'}) - @ApiResponse({status: 200, description:'Find current and all pay periods', type: PayPeriodBundleDto}) async getCurrentAndAll(@Query('date') date?: string): Promise { const [current, periods] = await Promise.all([ this.queryService.findCurrent(date), @@ -31,19 +27,11 @@ export class PayPeriodsController { } @Get("date/:date") - @ApiOperation({ summary: "Resolve a period by a date within it" }) - @ApiResponse({ status: 200, description: "Pay period found for the selected date", type: PayPeriodDto }) - @ApiNotFoundResponse({ description: "Pay period not found for the selected date" }) async findByDate(@Param("date") date: string) { return this.queryService.findByDate(date); } @Get(":year/:periodNumber") - @ApiOperation({ summary: "Find pay period by year and period number" }) - @ApiParam({ name: "year", type: Number, example: 2024 }) - @ApiParam({ name: "periodNumber", type: Number, example: 1, description: "1..26" }) - @ApiResponse({ status: 200, description: "Pay period found", type: PayPeriodDto }) - @ApiNotFoundResponse({ description: "Pay period not found" }) async findOneByYear( @Param("year", ParseIntPipe) year: number, @Param("periodNumber", ParseIntPipe) period_no: number, @@ -61,30 +49,19 @@ export class PayPeriodsController { @Get(':year/:periodNumber/:email') //@RolesAllowed(RoleEnum.SUPERVISOR) - @ApiOperation({ summary: 'Supervisor crew overview for a given pay period' }) - @ApiParam({ name: 'year', type: Number, example: 2024 }) - @ApiParam({ name: 'periodNumber', type: Number, example: 1, description: '1..26' }) - @ApiQuery({ name: 'includeSubtree', required: false, type: Boolean, example: false, description: 'Include indirect reports' }) - @ApiResponse({ status: 200, description: 'Crew overview', type: PayPeriodOverviewDto }) - @ApiNotFoundResponse({ description: 'Pay period not found' }) - async getCrewOverview( - @Param('year', ParseIntPipe) year: number, - @Param('periodNumber', ParseIntPipe) period_no: number, - @Param('email') email: string, - @Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false, + async getCrewOverview( @Req() req, + @Param('year', ParseIntPipe) year: number, + @Param('periodNumber', ParseIntPipe) period_no: number, + @Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false, ): Promise { + const email = req.user?.email; return this.queryService.getCrewOverview(year, period_no, email, include_subtree); } @Get('overview/:year/:periodNumber') - @ApiOperation({ summary: 'Detailed view of a pay period by year + number' }) - @ApiParam({ name: 'year', type: Number, example: 2024 }) - @ApiParam({ name: 'periodNumber', type: Number, example: 1, description: '1..26' }) - @ApiResponse({ status: 200, description: 'Pay period overview found', type: PayPeriodOverviewDto }) - @ApiNotFoundResponse({ description: 'Pay period not found' }) - async getOverviewByYear( - @Param('year', ParseIntPipe) year: number, - @Param('periodNumber', ParseIntPipe) period_no: number, + async getOverviewByYear( + @Param('year', ParseIntPipe) year: number, + @Param('periodNumber', ParseIntPipe) period_no: number, ): Promise { return this.queryService.getOverviewByYearPeriod(year, period_no); } diff --git a/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts b/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts index 3762ddb..650927d 100644 --- a/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts +++ b/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts @@ -1,5 +1,5 @@ import { Type } from "class-transformer"; -import { IsArray, IsBoolean, IsEmail, IsInt, IsOptional, ValidateNested } from "class-validator"; +import { IsArray, IsBoolean, IsEmail, IsInt, ValidateNested } from "class-validator"; export class BulkCrewApprovalItemDto { @IsInt() diff --git a/src/time-and-attendance/pay-period/dtos/bundle-pay-period.dto.ts b/src/time-and-attendance/pay-period/dtos/bundle-pay-period.dto.ts index 9c5a61f..ad84088 100644 --- a/src/time-and-attendance/pay-period/dtos/bundle-pay-period.dto.ts +++ b/src/time-and-attendance/pay-period/dtos/bundle-pay-period.dto.ts @@ -1,11 +1,6 @@ -import { ApiProperty } from "@nestjs/swagger"; import { PayPeriodDto } from "./pay-period.dto"; export class PayPeriodBundleDto { - - @ApiProperty({ type: PayPeriodDto, description: 'Current pay period (resolved from date)' }) current: PayPeriodDto; - - @ApiProperty({ type: [PayPeriodDto], description: 'All pay periods' }) periods: PayPeriodDto[]; } \ No newline at end of file diff --git a/src/time-and-attendance/pay-period/dtos/overview-employee-period.dto.ts b/src/time-and-attendance/pay-period/dtos/overview-employee-period.dto.ts index 1ea6937..4cae27b 100644 --- a/src/time-and-attendance/pay-period/dtos/overview-employee-period.dto.ts +++ b/src/time-and-attendance/pay-period/dtos/overview-employee-period.dto.ts @@ -1,27 +1,7 @@ -import { ApiProperty } from '@nestjs/swagger'; - export class EmployeePeriodOverviewDto { - // @ApiProperty({ - // example: 42, - // description: "Employees.id (clé primaire num.)", - // }) - // @Allow() - // @IsOptional() - // employee_id: number; - - email: string; - - @ApiProperty({ - example: 'Alex Dupont', - description: 'Nom complet de lemployé', - }) employee_name: string; - - @ApiProperty({ example: 40, description: 'pay-period`s regular hours' }) regular_hours: number; - - @ApiProperty({ example: 0, description: 'pay-period`s other hours' }) other_hours: { evening_hours: number; @@ -35,20 +15,9 @@ export class EmployeePeriodOverviewDto { vacation_hours: number; }; - total_hours: number; - - @ApiProperty({ example: 420.69, description: 'pay-period`s total expenses ($)' }) expenses: number; - - @ApiProperty({ example: 40, description: 'pay-period total mileages (km)' }) mileage: number; - - @ApiProperty({ - example: true, - description: 'Tous les timesheets de la période sont approuvés pour cet employé', - }) is_approved: boolean; - is_remote: boolean; } diff --git a/src/time-and-attendance/pay-period/dtos/overview-pay-period.dto.ts b/src/time-and-attendance/pay-period/dtos/overview-pay-period.dto.ts index 041fba3..3748eb8 100644 --- a/src/time-and-attendance/pay-period/dtos/overview-pay-period.dto.ts +++ b/src/time-and-attendance/pay-period/dtos/overview-pay-period.dto.ts @@ -1,46 +1,11 @@ -import { ApiProperty } from '@nestjs/swagger'; import { EmployeePeriodOverviewDto } from './overview-employee-period.dto'; export class PayPeriodOverviewDto { - @ApiProperty({ example: 1, description: 'Period number (1–26)' }) pay_period_no: number; - - @ApiProperty({ example: 2023, description: 'Calendar year of the period' }) pay_year: number; - - @ApiProperty({ - example: '2023-12-17', - type: String, - format: 'date', - description: "Period start date (YYYY-MM-DD)", - }) period_start: string; - - @ApiProperty({ - example: '2023-12-30', - type: String, - format: 'date', - description: "Period end date (YYYY-MM-DD)", - }) period_end: string; - - @ApiProperty({ - example: '2023-12-30', - type: String, - format: 'date', - description: "Period pay day(YYYY-MM-DD)", - }) payday: string; - - @ApiProperty({ - example: '2023-12-17 → 2023-12-30', - description: 'Human-readable label', - }) label: string; - - @ApiProperty({ - type: [EmployeePeriodOverviewDto], - description: 'Per-employee overview for the period', - }) employees_overview: EmployeePeriodOverviewDto[]; } diff --git a/src/time-and-attendance/pay-period/dtos/pay-period.dto.ts b/src/time-and-attendance/pay-period/dtos/pay-period.dto.ts index 4f7989b..a85f481 100644 --- a/src/time-and-attendance/pay-period/dtos/pay-period.dto.ts +++ b/src/time-and-attendance/pay-period/dtos/pay-period.dto.ts @@ -1,25 +1,8 @@ -import { ApiProperty } from "@nestjs/swagger"; - export class PayPeriodDto { - @ApiProperty({ example: 1, - description: 'numéro cyclique de la période entre 1 et 26' }) pay_period_no: number; - - @ApiProperty({ example: '2023-12-17', - type: String, format: 'date' }) period_start: string; - - @ApiProperty({ example: '2023-12-30', - type: String, format: 'date' }) period_end: string; - - @ApiProperty({ example: '2023-01-04', - type: String, format: 'date' }) payday: string; - - @ApiProperty({ example: 2023 }) pay_year: number; - - @ApiProperty({ example: '2023-12-17 → 2023-12-30' }) label: string; } \ No newline at end of file diff --git a/src/time-and-attendance/shared/helpers/date-time.helpers.ts b/src/time-and-attendance/shared/helpers/date-time.helpers.ts deleted file mode 100644 index 2076530..0000000 --- a/src/time-and-attendance/shared/helpers/date-time.helpers.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { BadRequestException } from "@nestjs/common"; - -export const hhmmFromLocal = (d: Date) => - `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`; - -export const toDateOnly = (s: string): Date => { - if (/^\d{4}-\d{2}-\d{2}$/.test(s)) { - const y = Number(s.slice(0,4)); - const m = Number(s.slice(5,7)) - 1; - const d = Number(s.slice(8,10)); - return new Date(y, m, d, 0, 0, 0, 0); - } - const dt = new Date(s); - if (Number.isNaN(dt.getTime())) throw new BadRequestException(`Invalid date: ${s}`); - return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate(), 0,0,0,0); -}; - -// export const toStringFromDate = (d: Date) => -// `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`; - - -export const toISOtoDateOnly = (iso: string): Date => { - const date = new Date(iso); - if (Number.isNaN(date.getTime())) { - throw new BadRequestException(`Invalid date: ${iso}`); - } - date.setHours(0, 0, 0, 0); - return date; -}; - -export const toISODateKey = (date: Date): string => date.toISOString().slice(0, 10); - -export const normalizeDates = (dates: string[]): string[] => - Array.from(new Set(dates.map((iso) => toISODateKey(toISOtoDateOnly(iso))))); \ No newline at end of file diff --git a/src/time-and-attendance/shared/interfaces/shifts.interface.ts b/src/time-and-attendance/shared/interfaces/shifts.interface.ts deleted file mode 100644 index 40f897e..0000000 --- a/src/time-and-attendance/shared/interfaces/shifts.interface.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface ShiftKey { - timesheet_id: number; - date: Date; - start_time: Date; - end_time: Date; - bank_code_id: number; - is_remote: boolean; - comment?: string | null; -} \ No newline at end of file diff --git a/src/time-and-attendance/shared/selects/expenses.select.ts b/src/time-and-attendance/shared/selects/expenses.select.ts deleted file mode 100644 index 540d98f..0000000 --- a/src/time-and-attendance/shared/selects/expenses.select.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const EXPENSE_SELECT = { - date: true, - amount: true, - mileage: true, - comment: true, - is_approved: true, - supervisor_comment: true, - bank_code: { select: { type: true } }, -} as const; - -export const EXPENSE_ASC_ORDER = { date: 'asc' as const }; \ No newline at end of file diff --git a/src/time-and-attendance/shared/selects/pay-periods.select.ts b/src/time-and-attendance/shared/selects/pay-periods.select.ts deleted file mode 100644 index a76f09b..0000000 --- a/src/time-and-attendance/shared/selects/pay-periods.select.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const PAY_PERIOD_SELECT = { - period_start: true, - period_end: true, -} as const; \ No newline at end of file diff --git a/src/time-and-attendance/shared/selects/shifts.select.ts b/src/time-and-attendance/shared/selects/shifts.select.ts deleted file mode 100644 index 8c738e1..0000000 --- a/src/time-and-attendance/shared/selects/shifts.select.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const SHIFT_SELECT = { - date: true, - start_time: true, - end_time: true, - comment: true, - is_approved: true, - is_remote: true, - bank_code: {select: { type: true } }, -} as const; - -export const SHIFT_ASC_ORDER = [{date: 'asc' as const}, {start_time: 'asc' as const}]; - diff --git a/src/time-and-attendance/shared/shared.module.ts b/src/time-and-attendance/shared/shared.module.ts deleted file mode 100644 index 0e26d7b..0000000 --- a/src/time-and-attendance/shared/shared.module.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { EmployeeTimesheetResolver } from "./utils/resolve-timesheet.utils"; -import { EmailToIdResolver } from "./utils/resolve-email-id.utils"; -import { BankCodesResolver } from "./utils/resolve-bank-type-id.utils"; -import { FullNameResolver } from "./utils/resolve-full-name.utils"; -import { PrismaModule } from "src/prisma/prisma.module"; -import { Module } from "@nestjs/common"; - -@Module({ -imports: [PrismaModule], -providers: [ - FullNameResolver, - EmailToIdResolver, - BankCodesResolver, - EmployeeTimesheetResolver, -], -exports: [ - FullNameResolver, - EmailToIdResolver, - BankCodesResolver, - EmployeeTimesheetResolver, -], -}) export class SharedModule {} \ No newline at end of file diff --git a/src/time-and-attendance/time-and-attendance.module.ts b/src/time-and-attendance/time-and-attendance.module.ts index 89de655..1d052df 100644 --- a/src/time-and-attendance/time-and-attendance.module.ts +++ b/src/time-and-attendance/time-and-attendance.module.ts @@ -4,7 +4,6 @@ import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-l import { ExpenseController } from "src/time-and-attendance/expenses/controllers/expense.controller"; import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service"; import { PayperiodsModule } from "src/time-and-attendance/pay-period/pay-periods.module"; -import { SharedModule } from "src/time-and-attendance/shared/shared.module"; import { SchedulePresetsController } from "src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller"; import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; @@ -14,16 +13,17 @@ import { ShiftsGetService } from "src/time-and-attendance/time-tracker/shifts/se import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service"; import { TimesheetController } from "src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service"; +import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @Module({ imports: [ - BusinessLogicsModule, - PayperiodsModule, - SharedModule, + BusinessLogicsModule, + PayperiodsModule, ], controllers: [ - TimesheetController, - ShiftController, + TimesheetController, + ShiftController, SchedulePresetsController, ExpenseController, ], @@ -35,6 +35,8 @@ import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-track SchedulePresetsUpsertService, SchedulePresetsGetService, SchedulePresetsApplyService, + EmailToIdResolver, + BankCodesResolver, ], exports: [], -}) export class TimeAndAttendanceModule{}; \ No newline at end of file +}) export class TimeAndAttendanceModule { }; \ No newline at end of file diff --git a/src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts b/src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts index b261a2f..b17863a 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/schedule-presets.module.ts @@ -1,6 +1,5 @@ import { Module } from "@nestjs/common"; -import { SharedModule } from "src/time-and-attendance/shared/shared.module"; import { SchedulePresetsController } from "src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller"; import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; @@ -8,7 +7,6 @@ import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-track @Module({ - imports: [SharedModule], controllers: [SchedulePresetsController], providers: [ SchedulePresetsUpsertService, diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts index 9815e10..c055373 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service.ts @@ -4,7 +4,7 @@ import { DATE_ISO_FORMAT } from "src/time-and-attendance/utils/constants.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { ApplyResult } from "src/time-and-attendance/utils/type.utils"; import { WEEKDAY } from "src/time-and-attendance/utils/mappers.utils"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @Injectable() diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts index 8b89c80..3bb3050 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service.ts @@ -2,7 +2,7 @@ import { PresetResponse, ShiftResponse } from "src/time-and-attendance/utils/typ import { PrismaService } from "src/prisma/prisma.service"; import { Injectable } from "@nestjs/common"; import { Prisma } from "@prisma/client"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @Injectable() export class SchedulePresetsGetService { diff --git a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts index 534082e..0eaef09 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service.ts @@ -3,9 +3,9 @@ import { CreatePresetResult, DeletePresetResult, UpdatePresetResult } from "src/ import { Prisma, Weekday } from "@prisma/client"; import { toHHmmFromDate } from "src/time-and-attendance/utils/date-time.utils"; import { PrismaService } from "src/prisma/prisma.service"; -import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils"; +import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @Injectable() export class SchedulePresetsUpsertService { diff --git a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts index 44261cb..59dc811 100644 --- a/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts +++ b/src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service.ts @@ -3,7 +3,7 @@ import { overlaps, toStringFromHHmm, toStringFromDate, toDateFromString, toHHmmF import { Injectable, BadRequestException, ConflictException, NotFoundException } from "@nestjs/common"; import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; import { PrismaService } from "src/prisma/prisma.service"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto"; import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto"; import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; diff --git a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts index 6ea81e2..ef7fdd6 100644 --- a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts +++ b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service.ts @@ -3,7 +3,7 @@ import { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/time-and-attendance/utils/co import { Injectable, NotFoundException } from "@nestjs/common"; import { TotalExpenses, TotalHours } from "src/time-and-attendance/utils/type.utils"; import { PrismaService } from "src/prisma/prisma.service"; -import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; @Injectable() export class GetTimesheetsOverviewService { diff --git a/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts index f559f6d..a12322a 100644 --- a/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts +++ b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts @@ -1,12 +1,10 @@ import { Module } from '@nestjs/common'; -import { SharedModule } from 'src/time-and-attendance/shared/shared.module'; import { TimesheetController } from 'src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller'; import { TimesheetArchiveService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service'; import { GetTimesheetsOverviewService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service'; @Module({ - imports: [SharedModule], controllers: [TimesheetController], providers: [ TimesheetArchiveService, diff --git a/src/time-and-attendance/utils/constants.utils.ts b/src/time-and-attendance/utils/constants.utils.ts index e2f20fc..2a53b28 100644 --- a/src/time-and-attendance/utils/constants.utils.ts +++ b/src/time-and-attendance/utils/constants.utils.ts @@ -6,6 +6,8 @@ export const ANCHOR_ISO = '2023-12-17'; export const PERIOD_DAYS = 14; export const PERIODS_PER_YEAR = 26; export const MS_PER_DAY = 86_400_000; +export const MS_PER_WEEK = 7 * 24 * 60 * 60 * 1000; + //REGEX CONSTANTS export const DATE_ISO_FORMAT = /^\d{4}-\d{2}-\d{2}$/; diff --git a/src/time-and-attendance/utils/date-time.utils.ts b/src/time-and-attendance/utils/date-time.utils.ts index 07ea016..cfba4e8 100644 --- a/src/time-and-attendance/utils/date-time.utils.ts +++ b/src/time-and-attendance/utils/date-time.utils.ts @@ -1,3 +1,4 @@ +import { BadRequestException } from "@nestjs/common"; import { ANCHOR_ISO, MS_PER_DAY, PERIODS_PER_YEAR, PERIOD_DAYS } from "src/time-and-attendance/utils/constants.utils"; //ensures the week starts from sunday @@ -88,4 +89,38 @@ export function listPayYear(pay_year: number, anchorISO = ANCHOR_ISO) { } export const overlaps = (a: { start: Date; end: Date }, b: { start: Date; end: Date }) => - !(a.end <= b.start || a.start >= b.end); \ No newline at end of file + !(a.end <= b.start || a.start >= b.end); + + +export const hhmmFromLocal = (d: Date) => + `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`; + +export const toDateOnly = (s: string): Date => { + if (/^\d{4}-\d{2}-\d{2}$/.test(s)) { + const y = Number(s.slice(0,4)); + const m = Number(s.slice(5,7)) - 1; + const d = Number(s.slice(8,10)); + return new Date(y, m, d, 0, 0, 0, 0); + } + const dt = new Date(s); + if (Number.isNaN(dt.getTime())) throw new BadRequestException(`Invalid date: ${s}`); + return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate(), 0,0,0,0); +}; + +// export const toStringFromDate = (d: Date) => +// `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`; + + +export const toISOtoDateOnly = (iso: string): Date => { + const date = new Date(iso); + if (Number.isNaN(date.getTime())) { + throw new BadRequestException(`Invalid date: ${iso}`); + } + date.setHours(0, 0, 0, 0); + return date; +}; + +export const toISODateKey = (date: Date): string => date.toISOString().slice(0, 10); + +export const normalizeDates = (dates: string[]): string[] => + Array.from(new Set(dates.map((iso) => toISODateKey(toISOtoDateOnly(iso))))); \ No newline at end of file diff --git a/src/time-and-attendance/shared/utils/resolve-bank-type-id.utils.ts b/src/time-and-attendance/utils/resolve-bank-type-id.utils.ts similarity index 100% rename from src/time-and-attendance/shared/utils/resolve-bank-type-id.utils.ts rename to src/time-and-attendance/utils/resolve-bank-type-id.utils.ts diff --git a/src/time-and-attendance/shared/utils/resolve-email-id.utils.ts b/src/time-and-attendance/utils/resolve-email-id.utils.ts similarity index 100% rename from src/time-and-attendance/shared/utils/resolve-email-id.utils.ts rename to src/time-and-attendance/utils/resolve-email-id.utils.ts diff --git a/src/time-and-attendance/shared/utils/resolve-full-name.utils.ts b/src/time-and-attendance/utils/resolve-full-name.utils.ts similarity index 100% rename from src/time-and-attendance/shared/utils/resolve-full-name.utils.ts rename to src/time-and-attendance/utils/resolve-full-name.utils.ts diff --git a/src/time-and-attendance/shared/utils/resolve-shifts-id.utils.ts b/src/time-and-attendance/utils/resolve-shifts-id.utils.ts similarity index 93% rename from src/time-and-attendance/shared/utils/resolve-shifts-id.utils.ts rename to src/time-and-attendance/utils/resolve-shifts-id.utils.ts index 4d9d313..e76d144 100644 --- a/src/time-and-attendance/shared/utils/resolve-shifts-id.utils.ts +++ b/src/time-and-attendance/utils/resolve-shifts-id.utils.ts @@ -1,7 +1,7 @@ import { Prisma, PrismaClient } from "@prisma/client"; import { NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; -import { ShiftKey } from "../interfaces/shifts.interface"; +import { ShiftKey } from "src/time-and-attendance/utils/type.utils"; type Tx = Prisma.TransactionClient | PrismaClient; diff --git a/src/time-and-attendance/shared/utils/resolve-timesheet.utils.ts b/src/time-and-attendance/utils/resolve-timesheet.utils.ts similarity index 100% rename from src/time-and-attendance/shared/utils/resolve-timesheet.utils.ts rename to src/time-and-attendance/utils/resolve-timesheet.utils.ts diff --git a/src/time-and-attendance/utils/selects.utils.ts b/src/time-and-attendance/utils/selects.utils.ts index 3dbf127..fd4a356 100644 --- a/src/time-and-attendance/utils/selects.utils.ts +++ b/src/time-and-attendance/utils/selects.utils.ts @@ -47,3 +47,34 @@ export const leaveRequestsSelect = { } }, } satisfies Prisma.LeaveRequestsSelect; + + +export const EXPENSE_SELECT = { + date: true, + amount: true, + mileage: true, + comment: true, + is_approved: true, + supervisor_comment: true, + bank_code: { select: { type: true } }, +} as const; + +export const EXPENSE_ASC_ORDER = { date: 'asc' as const }; + +export const PAY_PERIOD_SELECT = { + period_start: true, + period_end: true, +} as const; + +export const SHIFT_SELECT = { + date: true, + start_time: true, + end_time: true, + comment: true, + is_approved: true, + is_remote: true, + bank_code: {select: { type: true } }, +} as const; + +export const SHIFT_ASC_ORDER = [{date: 'asc' as const}, {start_time: 'asc' as const}]; + diff --git a/src/time-and-attendance/utils/type.utils.ts b/src/time-and-attendance/utils/type.utils.ts index 5e846c1..412566a 100644 --- a/src/time-and-attendance/utils/type.utils.ts +++ b/src/time-and-attendance/utils/type.utils.ts @@ -1,5 +1,4 @@ -import { Prisma } from "@prisma/client"; -import { WeekOvertimeSummary } from "src/time-and-attendance/domains/services/overtime.service"; +import { Prisma, PrismaClient } from "@prisma/client"; import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto"; import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; @@ -80,4 +79,32 @@ export type ApplyResult = { export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>; -export type UpsertAction = 'create' | 'update' | 'delete'; \ No newline at end of file +export type UpsertAction = 'create' | 'update' | 'delete'; + +export type Tx = Prisma.TransactionClient | PrismaClient; + +export type WeekOvertimeSummary = { + week_start:string; + week_end: string; + week_total_hours: number; + weekly_overtime: number; + daily_overtime_kept: number; + total_overtime: number; + breakdown: Array<{ + date:string; + day_hours: number; + day_overtime: number; + daily_kept: number; + running_total_before: number; + }>; +}; + +export interface ShiftKey { + timesheet_id: number; + date: Date; + start_time: Date; + end_time: Date; + bank_code_id: number; + is_remote: boolean; + comment?: string | null; +} \ No newline at end of file From 7ee87d84098bc1527201ef59f9c58b2c8bd75464 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 3 Nov 2025 12:08:54 -0500 Subject: [PATCH 10/12] feat(timesheets): implement role guards for timesheets --- .../expenses/controllers/expense.controller.ts | 3 +-- .../controllers/leave-requests.controller.ts | 11 +---------- .../timesheets/controllers/timesheet.controller.ts | 4 ++++ 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/time-and-attendance/expenses/controllers/expense.controller.ts b/src/time-and-attendance/expenses/controllers/expense.controller.ts index 34b83e9..5f9a7ff 100644 --- a/src/time-and-attendance/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/expenses/controllers/expense.controller.ts @@ -16,8 +16,7 @@ export class ExpenseController { } @Patch('update') - update( - @Body() body: { update :{ id: number; dto: updateExpenseDto }}): Promise{ + update(@Body() body: { update :{ id: number; dto: updateExpenseDto }}): Promise{ return this.upsert_service.updateExpense(body.update); } diff --git a/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts b/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts index 53c8e47..62a14c4 100644 --- a/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts +++ b/src/time-and-attendance/leave-requests/controllers/leave-requests.controller.ts @@ -14,16 +14,7 @@ export class LeaveRequestController { async upsertLeaveRequest(@Body() dto: UpsertLeaveRequestDto) { const { action, leave_requests } = await this.leave_service.handle(dto); return { action, leave_requests }; - }q - - //TODO: - /* - @Get('archive') - findAllArchived(){...} - - @Get('archive/:id') - findOneArchived(id){...} - */ + } } diff --git a/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts index 90cf522..220d068 100644 --- a/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts @@ -1,11 +1,15 @@ import { Controller, Get, ParseIntPipe, Query, Req, UnauthorizedException} from "@nestjs/common"; +import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service"; +import { Roles as RoleEnum } from '.prisma/client'; + @Controller('timesheets') export class TimesheetController { constructor( private readonly timesheetOverview: GetTimesheetsOverviewService ){} @Get() + @RolesAllowed(RoleEnum.SUPERVISOR, RoleEnum.HR, RoleEnum.ACCOUNTING, RoleEnum.ADMIN) async getTimesheetByIds( @Req() req, @Query('year', ParseIntPipe) year: number, From 5268737bd1a4bd61b7ef5d8e284f9bae92359e6a Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 3 Nov 2025 12:11:13 -0500 Subject: [PATCH 11/12] feat(expenses): implement role guards --- .../expenses/controllers/expense.controller.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/time-and-attendance/expenses/controllers/expense.controller.ts b/src/time-and-attendance/expenses/controllers/expense.controller.ts index 5f9a7ff..fb81eb2 100644 --- a/src/time-and-attendance/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/expenses/controllers/expense.controller.ts @@ -3,12 +3,15 @@ import { CreateExpenseResult, UpdateExpenseResult } from "src/time-and-attendanc import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service"; import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto"; import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; +import { RolesAllowed } from "src/common/decorators/roles.decorators"; +import { Roles as RoleEnum } from '.prisma/client'; @Controller('expense') export class ExpenseController { constructor( private readonly upsert_service: ExpenseUpsertService ){} @Post('create') + @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) create( @Req() req, @Body() dto: ExpenseDto): Promise{ const email = req.user?.email; if(!email) throw new UnauthorizedException('Unauthorized User'); @@ -16,12 +19,16 @@ export class ExpenseController { } @Patch('update') + @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) update(@Body() body: { update :{ id: number; dto: updateExpenseDto }}): Promise{ return this.upsert_service.updateExpense(body.update); } @Delete('delete/:expense_id') + @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) remove(@Param('expense_id') expense_id: number) { return this.upsert_service.deleteExpense(expense_id); } -} \ No newline at end of file +} + + From bdbec4f68c541d25d63dc24b77f48d9f90f5d44f Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 3 Nov 2025 14:14:09 -0500 Subject: [PATCH 12/12] feat(pay-period): added approval of timesheets, shifts and expenses by bulk. added route to controller --- docs/swagger/swagger-spec.json | 92 +++++++++----- .../services/expense-upsert.service.ts | 3 +- .../controllers/pay-periods.controller.ts | 47 +++---- .../pay-period/dtos/bulk-crew-approval.dto.ts | 3 - .../pay-period/pay-periods.module.ts | 12 +- .../services/pay-periods-command.service.ts | 119 +++++++++--------- .../controller/schedule-presets.controller.ts | 27 ++-- .../shifts/controllers/shift.controller.ts | 13 +- .../controllers/timesheet.controller.ts | 7 +- .../services/timesheet-approval.service.ts | 4 +- .../timesheets/timesheets.module.ts | 5 + 11 files changed, 177 insertions(+), 155 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 8aa7528..4e6ef5a 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -162,7 +162,31 @@ ] } }, - "/pay-periods/{year}/{periodNumber}/{email}": { + "/pay-periods/crew/pay-period-approval": { + "patch": { + "operationId": "PayPeriodsController_bulkApproval", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BulkCrewApprovalDto" + } + } + } + }, + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "PayPeriods" + ] + } + }, + "/pay-periods/crew/{year}/{periodNumber}": { "get": { "operationId": "PayPeriodsController_getCrewOverview", "parameters": [ @@ -224,6 +248,37 @@ ] } }, + "/timesheets": { + "get": { + "operationId": "TimesheetController_getTimesheetByIds", + "parameters": [ + { + "name": "year", + "required": true, + "in": "query", + "schema": { + "type": "number" + } + }, + { + "name": "period_number", + "required": true, + "in": "query", + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Timesheet" + ] + } + }, "/preferences": { "patch": { "operationId": "PreferencesController_updatePreferences", @@ -257,37 +312,6 @@ ] } }, - "/timesheets": { - "get": { - "operationId": "TimesheetController_getTimesheetByIds", - "parameters": [ - { - "name": "year", - "required": true, - "in": "query", - "schema": { - "type": "number" - } - }, - { - "name": "period_number", - "required": true, - "in": "query", - "schema": { - "type": "number" - } - } - ], - "responses": { - "200": { - "description": "" - } - }, - "tags": [ - "Timesheet" - ] - } - }, "/shift/create": { "post": { "operationId": "ShiftController_createBatch", @@ -609,6 +633,10 @@ } }, "schemas": { + "BulkCrewApprovalDto": { + "type": "object", + "properties": {} + }, "PreferencesDto": { "type": "object", "properties": {} diff --git a/src/time-and-attendance/expenses/services/expense-upsert.service.ts b/src/time-and-attendance/expenses/services/expense-upsert.service.ts index d48ea50..7f16064 100644 --- a/src/time-and-attendance/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/expenses/services/expense-upsert.service.ts @@ -1,7 +1,6 @@ - import { CreateExpenseResult, UpdateExpensePayload, UpdateExpenseResult, DeleteExpenseResult, NormalizedExpense } from "src/time-and-attendance/utils/type.utils"; import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils"; -import { Injectable, NotFoundException, Req } from "@nestjs/common"; +import { Injectable, NotFoundException } from "@nestjs/common"; import { expense_select } from "src/time-and-attendance/utils/selects.utils"; import { PrismaService } from "src/prisma/prisma.service"; import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; diff --git a/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts b/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts index 65ed977..1d43791 100644 --- a/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts +++ b/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts @@ -1,10 +1,9 @@ -import { Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Query, Req } from "@nestjs/common"; -import { PayPeriodDto } from "../dtos/pay-period.dto"; +import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query, Req, UnauthorizedException } from "@nestjs/common"; import { PayPeriodOverviewDto } from "../dtos/overview-pay-period.dto"; import { PayPeriodsQueryService } from "../services/pay-periods-query.service"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { Roles as RoleEnum } from '.prisma/client'; -// import { PayPeriodsCommandService } from "../services/pay-periods-command.service"; +import { PayPeriodsCommandService } from "../services/pay-periods-command.service"; import { PayPeriodBundleDto } from "../dtos/bundle-pay-period.dto"; import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto"; @@ -14,8 +13,8 @@ export class PayPeriodsController { constructor( private readonly queryService: PayPeriodsQueryService, - // private readonly commandService: PayPeriodsCommandService, - ) {} + private readonly commandService: PayPeriodsCommandService, + ) { } @Get('current-and-all') async getCurrentAndAll(@Query('date') date?: string): Promise { @@ -39,42 +38,32 @@ export class PayPeriodsController { return this.queryService.findOneByYearPeriod(year, period_no); } - // @Patch("crew/bulk-approval") - // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) - // @ApiOperation({ summary: "Approve all selected timesheets in the period" }) - // @ApiResponse({ status: 200, description: "Pay period approved" }) - // async bulkApproval(@Body() dto: BulkCrewApprovalDto) { - // return this.commandService.bulkApproveCrew(dto); - // } + @Patch("crew/pay-period-approval") + @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) + async bulkApproval(@Req() req, @Body() dto: BulkCrewApprovalDto) { + const email = req.user?.email; + if(!email) throw new UnauthorizedException(`Session infos not found`); + return this.commandService.bulkApproveCrew(email, dto); + } - @Get(':year/:periodNumber/:email') - //@RolesAllowed(RoleEnum.SUPERVISOR) - async getCrewOverview( @Req() req, + @Get('crew/:year/:periodNumber') + @RolesAllowed(RoleEnum.SUPERVISOR) + async getCrewOverview(@Req() req, @Param('year', ParseIntPipe) year: number, @Param('periodNumber', ParseIntPipe) period_no: number, @Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false, ): Promise { const email = req.user?.email; + if(!email) throw new UnauthorizedException(`Session infos not found`); return this.queryService.getCrewOverview(year, period_no, email, include_subtree); } @Get('overview/:year/:periodNumber') - async getOverviewByYear( - @Param('year', ParseIntPipe) year: number, + @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) + async getOverviewByYear( + @Param('year', ParseIntPipe) year: number, @Param('periodNumber', ParseIntPipe) period_no: number, ): Promise { return this.queryService.getOverviewByYearPeriod(year, period_no); } - - - //_____________________________________________________________________________________________ - // Deprecated or unused methods - //_____________________________________________________________________________________________ - - // @Get() - // @ApiOperation({ summary: 'Find all pay period' }) - // @ApiResponse({status: 200,description: 'List of pay period found', type: PayPeriodDto, isArray: true }) - // async findAll(): Promise { - // return this.queryService.findAll(); - // } } diff --git a/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts b/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts index 650927d..4ba5527 100644 --- a/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts +++ b/src/time-and-attendance/pay-period/dtos/bulk-crew-approval.dto.ts @@ -16,9 +16,6 @@ export class BulkCrewApprovalItemDto { } export class BulkCrewApprovalDto { - @IsEmail() - supervisor_email: string; - @IsBoolean() include_subtree: boolean = false; diff --git a/src/time-and-attendance/pay-period/pay-periods.module.ts b/src/time-and-attendance/pay-period/pay-periods.module.ts index 3670c72..f971084 100644 --- a/src/time-and-attendance/pay-period/pay-periods.module.ts +++ b/src/time-and-attendance/pay-period/pay-periods.module.ts @@ -1,10 +1,20 @@ import { PayPeriodsQueryService } from "./services/pay-periods-query.service"; import { PayPeriodsController } from "./controllers/pay-periods.controller"; import { Module } from "@nestjs/common"; +import { PayPeriodsCommandService } from "src/time-and-attendance/pay-period/services/pay-periods-command.service"; +import { TimesheetsModule } from "src/time-and-attendance/time-tracker/timesheets/timesheets.module"; +import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; +import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service"; @Module({ + imports:[TimesheetsModule], controllers: [PayPeriodsController], - providers: [PayPeriodsQueryService], + providers: [ + PayPeriodsQueryService, + PayPeriodsCommandService, + EmailToIdResolver, + TimesheetApprovalService, + ], }) export class PayperiodsModule {} \ No newline at end of file diff --git a/src/time-and-attendance/pay-period/services/pay-periods-command.service.ts b/src/time-and-attendance/pay-period/services/pay-periods-command.service.ts index 960e1ef..6c48fe2 100644 --- a/src/time-and-attendance/pay-period/services/pay-periods-command.service.ts +++ b/src/time-and-attendance/pay-period/services/pay-periods-command.service.ts @@ -1,71 +1,72 @@ -// import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from "@nestjs/common"; -// import { PrismaService } from "src/prisma/prisma.service"; -// import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto"; -// import { PayPeriodsQueryService } from "./pay-periods-query.service"; -// import { TimesheetApprovalService } from "src/modules/timesheets/services/timesheet-approval.service"; +import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from "@nestjs/common"; +import { PrismaService } from "src/prisma/prisma.service"; +import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto"; +import { PayPeriodsQueryService } from "./pay-periods-query.service"; +import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service"; -// @Injectable() -// export class PayPeriodsCommandService { -// constructor( -// private readonly prisma: PrismaService, -// private readonly timesheets_approval: TimesheetApprovalService, -// private readonly query: PayPeriodsQueryService, -// ) {} +@Injectable() +export class PayPeriodsCommandService { + constructor( + private readonly prisma: PrismaService, + private readonly timesheets_approval: TimesheetApprovalService, + private readonly query: PayPeriodsQueryService, + ) {} -// //function to approve pay-periods according to selected crew members -// async bulkApproveCrew(dto:BulkCrewApprovalDto): Promise<{updated: number}> { -// const { supervisor_email, include_subtree, items } = dto; -// if(!items?.length) throw new BadRequestException('no items to process'); + //function to approve pay-periods according to selected crew members + async bulkApproveCrew(email: string, dto:BulkCrewApprovalDto): Promise<{updated: number}> { + const { include_subtree, items } = dto; + if(!items?.length) throw new BadRequestException('no items to process'); -// //fetch and validate supervisor status -// const supervisor = await this.query.getSupervisor(supervisor_email); -// if(!supervisor) throw new NotFoundException('No employee record linked to current user'); -// if(!supervisor.is_supervisor) throw new ForbiddenException('Employee is not a supervisor'); + //fetch and validate supervisor status + const supervisor = await this.query.getSupervisor(email); + if(!supervisor) throw new NotFoundException('No employee record linked to current user'); + if(!supervisor.is_supervisor) throw new ForbiddenException('Employee is not a supervisor'); -// //fetches emails of crew members linked to supervisor -// const crew_emails = await this.query.resolveCrewEmails(supervisor.id, include_subtree); + //fetches emails of crew members linked to supervisor + const crew_emails = await this.query.resolveCrewEmails(supervisor.id, include_subtree); -// for(const item of items) { -// if(!crew_emails.has(item.employee_email)) { -// throw new ForbiddenException(`Employee ${item.employee_email} not in supervisor crew`); -// } -// } + for(const item of items) { + if(!crew_emails.has(item.employee_email)) { + throw new ForbiddenException(`Employee ${item.employee_email} not in supervisor crew`); + } + } -// const period_cache = new Map(); -// const getPeriod = async (y:number, no: number) => { -// const key = `${y}-${no}`; -// if(!period_cache.has(key)) return period_cache.get(key)!; -// const period = await this.query.getPeriodWindow(y,no); -// if(!period) throw new NotFoundException(`Pay period ${y}-${no} not found`); -// period_cache.set(key, period); -// return period; -// }; + const period_cache = new Map(); + const getPeriod = async (year:number, period_no: number) => { + const key = `${year}-${period_no}`; + if(period_cache.has(key)) return period_cache.get(key)!; -// let updated = 0; + const period = await this.query.getPeriodWindow(year,period_no); + if(!period) throw new NotFoundException(`Pay period ${year}-${period_no} not found`); + period_cache.set(key, period); + return period; + }; -// await this.prisma.$transaction(async (transaction) => { -// for(const item of items) { -// const { period_start, period_end } = await getPeriod(item.pay_year, item.period_no); + let updated = 0; -// const t_sheets = await transaction.timesheets.findMany({ -// where: { -// employee: { user: { email: item.employee_email } }, -// OR: [ -// {shift : { some: { date: { gte: period_start, lte: period_end } } } }, -// {expense: { some: { date: { gte: period_start, lte: period_end } } } }, -// ], -// }, -// select: { id: true }, -// }); + await this.prisma.$transaction(async (transaction) => { + for(const item of items) { + const { period_start, period_end } = await getPeriod(item.pay_year, item.period_no); -// for(const { id } of t_sheets) { -// await this.timesheets_approval.updateApprovalWithTransaction(transaction, id, item.approve); -// updated++; -// } + const t_sheets = await transaction.timesheets.findMany({ + where: { + employee: { user: { email: item.employee_email } }, + OR: [ + {shift : { some: { date: { gte: period_start, lte: period_end } } } }, + {expense: { some: { date: { gte: period_start, lte: period_end } } } }, + ], + }, + select: { id: true }, + }); + + for(const { id } of t_sheets) { + await this.timesheets_approval.cascadeApprovalWithtx(transaction, id, item.approve); + updated++; + } -// } -// }); -// return {updated}; -// } -// } \ No newline at end of file + } + }); + return {updated}; + } +} \ No newline at end of file diff --git a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts index 56fc28a..042b8df 100644 --- a/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller.ts @@ -1,10 +1,11 @@ import { Controller, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch, Req } from "@nestjs/common"; +import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; import { SchedulePresetsUpdateDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto"; import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; - +import { Roles as RoleEnum } from '.prisma/client'; @Controller('schedule-presets') export class SchedulePresetsController { @@ -16,45 +17,39 @@ export class SchedulePresetsController { //used to create a schedule preset @Post('create') - async createPreset( @Req() req, - @Body() dto: SchedulePresetsDto, - ) { + @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) + async createPreset( @Req() req, @Body() dto: SchedulePresetsDto ) { const email = req.user?.email; return await this.upsertService.createPreset(email, dto); } //used to update an already existing schedule preset @Patch('update/:preset_id') - async updatePreset( - @Param('preset_id', ParseIntPipe) preset_id: number, - @Body() dto: SchedulePresetsUpdateDto, - ) { + @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) + async updatePreset( @Param('preset_id', ParseIntPipe) preset_id: number,@Body() dto: SchedulePresetsUpdateDto ) { return await this.upsertService.updatePreset(preset_id, dto); } //used to delete a schedule preset @Delete('delete/:preset_id') - async deletePreset( - @Param('preset_id') preset_id: number, - ) { + @RolesAllowed(RoleEnum.ADMIN) + async deletePreset( @Param('preset_id') preset_id: number ) { return await this.upsertService.deletePreset(preset_id); } //used to show the list of available schedule presets @Get('find-list') + @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) async findListById( @Req() req) { const email = req.user?.email; return this.getService.getSchedulePresets(email); } - //used to apply a preset to a timesheet @Post('apply-presets') - async applyPresets( @Req() req, - @Query('preset') preset_name: string, - @Query('start') start_date: string, - ) { + @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) + async applyPresets( @Req() req, @Query('preset') preset_name: string, @Query('start') start_date: string ) { const email = req.user?.email; if(!preset_name?.trim()) throw new BadRequestException('Query "preset" is required'); if(!start_date?.trim()) throw new BadRequestException('Query "start" is required YYYY-MM-DD'); diff --git a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts index 07395f9..767191b 100644 --- a/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/time-tracker/shifts/controllers/shift.controller.ts @@ -3,16 +3,16 @@ import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service"; import { CreateShiftResult, UpdateShiftResult } from "src/time-and-attendance/utils/type.utils"; - +import { Roles as RoleEnum } from '.prisma/client'; +import { RolesAllowed } from "src/common/decorators/roles.decorators"; @Controller('shift') export class ShiftController { constructor( private readonly upsert_service: ShiftsUpsertService ){} @Post('create') - createBatch( - @Req() req, - @Body()dtos: ShiftDto[]): Promise { + @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) + createBatch( @Req() req, @Body()dtos: ShiftDto[]): Promise { const email = req.user?.email; const list = Array.isArray(dtos) ? dtos : []; if(list.length === 0) throw new BadRequestException('Body is missing or invalid (create shifts)'); @@ -22,14 +22,15 @@ export class ShiftController { //change Body to receive dtos @Patch('update') - updateBatch( - @Body() dtos: UpdateShiftDto[]): Promise{ + @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) + updateBatch( @Body() dtos: UpdateShiftDto[]): Promise{ const list = Array.isArray(dtos) ? dtos: []; if(list.length === 0) throw new BadRequestException('Body is missing or invalid (update shifts)'); return this.upsert_service.updateShifts(dtos); } @Delete(':shift_id') + @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN) remove(@Param('shift_id') shift_id: number ) { return this.upsert_service.deleteShift(shift_id); } diff --git a/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts index 220d068..ffde38b 100644 --- a/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller.ts @@ -11,12 +11,9 @@ export class TimesheetController { @Get() @RolesAllowed(RoleEnum.SUPERVISOR, RoleEnum.HR, RoleEnum.ACCOUNTING, RoleEnum.ADMIN) async getTimesheetByIds( - @Req() req, - @Query('year', ParseIntPipe) year: number, - @Query('period_number', ParseIntPipe) period_number: number, - ) { + @Req() req, @Query('year', ParseIntPipe) year:number, @Query('period_number', ParseIntPipe) period_number: number) { const email = req.user?.email; - if(!email) throw new UnauthorizedException('Unauthorized User'); + if(!email) throw new UnauthorizedException('Unauthorized User');  return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(email, year, period_number); } } diff --git a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts index 088b5b7..84756c2 100644 --- a/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts +++ b/src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service.ts @@ -27,11 +27,11 @@ import { Injectable } from "@nestjs/common"; const timesheet = await this.updateApprovalWithTransaction(transaction, timesheetId, isApproved); await transaction.shifts.updateMany({ where: { timesheet_id: timesheetId }, - data: { is_approved: isApproved }, + data: { is_approved: isApproved }, }); await transaction.expenses.updateManyAndReturn({ where: { timesheet_id: timesheetId }, - data: { is_approved: isApproved }, + data: { is_approved: isApproved }, }); return timesheet; } diff --git a/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts index a12322a..761a78e 100644 --- a/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts +++ b/src/time-and-attendance/time-tracker/timesheets/timesheets.module.ts @@ -1,14 +1,19 @@ import { Module } from '@nestjs/common'; import { TimesheetController } from 'src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller'; +import { TimesheetApprovalService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service'; import { TimesheetArchiveService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service'; import { GetTimesheetsOverviewService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service'; +import { EmailToIdResolver } from 'src/time-and-attendance/utils/resolve-email-id.utils'; @Module({ + controllers: [TimesheetController], providers: [ TimesheetArchiveService, GetTimesheetsOverviewService, + TimesheetApprovalService, + EmailToIdResolver, ], exports: [], })