From 3984540edbf8cc3cda81bb6f3da9403aea6c339d Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 3 Oct 2025 10:22:27 -0400 Subject: [PATCH] fix(leaves and expenses): import name fixes --- docs/swagger/swagger-spec.json | 303 +----------------- .../07-leave-requests-future.ts | 27 +- .../08-leave-requests-archive.ts | 54 ++-- src/modules/archival/archival.module.ts | 2 +- .../leave-requests-archive.controller.ts | 32 +- .../archival/services/archival.service.ts | 4 +- .../services/expenses-command.service.ts | 12 +- .../expenses.types.interfaces.ts | 3 +- src/modules/expenses/utils/expenses.utils.ts | 4 +- .../dtos/leave-request-view.dto.ts | 2 +- .../leave-requests/leave-requests.module.ts | 2 + .../mappers/leave-requests.mapper.ts | 6 +- .../holiday-leave-requests.service.ts | 20 +- .../utils/leave-request.transform.ts | 41 +-- 14 files changed, 93 insertions(+), 419 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 68cdc3d..8f2bd52 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -77,30 +77,6 @@ ] } }, - "/archives/leaveRequests": { - "get": { - "operationId": "LeaveRequestsArchiveController_findOneArchived", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], - "responses": { - "200": { - "description": "Archived leaveRequest found" - } - }, - "summary": "Fetch leaveRequest in archives with its Id", - "tags": [ - "LeaveRequests Archives" - ] - } - }, "/archives/shifts": { "get": { "operationId": "ShiftsArchiveController_findOneArchived", @@ -1256,215 +1232,22 @@ ] } }, - "/leave-requests": { + "/leave-requests/holiday": { "post": { - "operationId": "LeaveRequestController_create", + "operationId": "LeaveRequestController_upsertHoliday", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateLeaveRequestsDto" + "$ref": "#/components/schemas/UpsertHolidayDto" } } } }, "responses": { "201": { - "description": "Leave request created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateLeaveRequestsDto" - } - } - } - }, - "400": { - "description": "Incomplete task or invalid data" - } - }, - "security": [ - { - "access-token": [] - } - ], - "summary": "Create leave request", - "tags": [ - "Leave Requests" - ] - }, - "get": { - "operationId": "LeaveRequestController_findAll", - "parameters": [], - "responses": { - "201": { - "description": "List of Leave requests found", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LeaveRequestViewDto" - } - } - } - } - }, - "400": { - "description": "List of leave request not found" - } - }, - "security": [ - { - "access-token": [] - } - ], - "summary": "Find all leave request", - "tags": [ - "Leave Requests" - ] - } - }, - "/leave-requests/{id}": { - "get": { - "operationId": "LeaveRequestController_findOne", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], - "responses": { - "201": { - "description": "Leave request found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LeaveRequestViewDto" - } - } - } - }, - "400": { - "description": "Leave request not found" - } - }, - "security": [ - { - "access-token": [] - } - ], - "summary": "Find leave request", - "tags": [ - "Leave Requests" - ] - }, - "patch": { - "operationId": "LeaveRequestController_update", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateLeaveRequestsDto" - } - } - } - }, - "responses": { - "201": { - "description": "Leave request updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LeaveRequestViewDto" - } - } - } - }, - "400": { - "description": "Leave request not found" - } - }, - "security": [ - { - "access-token": [] - } - ], - "summary": "Update leave request", - "tags": [ - "Leave Requests" - ] - }, - "delete": { - "operationId": "LeaveRequestController_remove", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], - "responses": { - "201": { - "description": "Leave request deleted", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateLeaveRequestsDto" - } - } - } - }, - "400": { - "description": "Leave request not found" - } - }, - "security": [ - { - "access-token": [] - } - ], - "summary": "Delete leave request", - "tags": [ - "Leave Requests" - ] - } - }, - "/leave-requests/approval/{id}": { - "patch": { - "operationId": "LeaveRequestController_updateApproval", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { - "type": "number" - } - } - ], - "responses": { - "200": { "description": "" } }, @@ -2691,88 +2474,10 @@ } } }, - "CreateLeaveRequestsDto": { - "type": "object", - "properties": { - "bank_code_id": { - "type": "number", - "example": 7, - "description": "ID number of a leave-request code (link with bank-codes)" - }, - "leave_type": { - "type": "string", - "example": "Sick or Vacation or Unpaid or Bereavement or Parental or Legal", - "description": "type of leave request for an accounting perception" - }, - "start_date_time": { - "type": "string", - "example": "22/06/2463", - "description": "Leave request`s start date" - }, - "end_date_time": { - "type": "string", - "example": "25/03/3019", - "description": "Leave request`s end date" - }, - "comment": { - "type": "string", - "example": "My precious", - "description": "Leave request`s comment" - }, - "approval_status": { - "type": "string", - "example": "True or False or Pending or Denied or Cancelled or Escalated", - "description": "Leave request`s approval status" - } - }, - "required": [ - "bank_code_id", - "leave_type", - "start_date_time", - "end_date_time", - "comment", - "approval_status" - ] - }, - "LeaveRequestViewDto": { + "UpsertHolidayDto": { "type": "object", "properties": {} }, - "UpdateLeaveRequestsDto": { - "type": "object", - "properties": { - "bank_code_id": { - "type": "number", - "example": 7, - "description": "ID number of a leave-request code (link with bank-codes)" - }, - "leave_type": { - "type": "string", - "example": "Sick or Vacation or Unpaid or Bereavement or Parental or Legal", - "description": "type of leave request for an accounting perception" - }, - "start_date_time": { - "type": "string", - "example": "22/06/2463", - "description": "Leave request`s start date" - }, - "end_date_time": { - "type": "string", - "example": "25/03/3019", - "description": "Leave request`s end date" - }, - "comment": { - "type": "string", - "example": "My precious", - "description": "Leave request`s comment" - }, - "approval_status": { - "type": "string", - "example": "True or False or Pending or Denied or Cancelled or Escalated", - "description": "Leave request`s approval status" - } - } - }, "CreateBankCodeDto": { "type": "object", "properties": { diff --git a/prisma/mock-seeds-scripts/07-leave-requests-future.ts b/prisma/mock-seeds-scripts/07-leave-requests-future.ts index c5dc5e2..b96c63d 100644 --- a/prisma/mock-seeds-scripts/07-leave-requests-future.ts +++ b/prisma/mock-seeds-scripts/07-leave-requests-future.ts @@ -1,14 +1,14 @@ import { PrismaClient, Prisma, LeaveTypes, LeaveApprovalStatus } from '@prisma/client'; if (process.env.SKIP_LEAVE_REQUESTS === 'true') { - console.log("⏭ Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)"); + console.log('?? Seed leave-requests ignor (SKIP_LEAVE_REQUESTS=true)'); process.exit(0); } const prisma = new PrismaClient(); function dateOn(y: number, m: number, d: number) { - // stocke une date (pour @db.Date) à minuit UTC + // stocke une date (@db.Date) minuit UTC return new Date(Date.UTC(y, m - 1, d, 0, 0, 0)); } @@ -19,7 +19,7 @@ async function main() { const employees = await prisma.employees.findMany({ select: { id: true } }); const bankCodes = await prisma.bankCodes.findMany({ where: { categorie: 'LEAVE' }, - select: { id: true }, + select: { id: true, type: true }, }); if (!employees.length || !bankCodes.length) { @@ -44,30 +44,31 @@ async function main() { LeaveApprovalStatus.ESCALATED, ]; - const futureMonths = [8, 9, 10, 11, 12]; // Août→Déc (1-based) + const futureMonths = [8, 9, 10, 11, 12]; // Aot ? Dc. (1-based) - // ✅ typer rows pour éviter never[] const rows: Prisma.LeaveRequestsCreateManyInput[] = []; for (let i = 0; i < 10; i++) { const emp = employees[i % employees.length]; const m = futureMonths[i % futureMonths.length]; - const start = dateOn(year, m, 5 + i); // 5..14 - if (start <= today) continue; // garantir "futur" + const date = dateOn(year, m, 5 + i); // 5..14 + if (date <= today) continue; // garantir futur - const end = Math.random() < 0.5 ? null : dateOn(year, m, 6 + i); const type = types[i % types.length]; const status = statuses[i % statuses.length]; const bc = bankCodes[i % bankCodes.length]; + const requestedHours = 4 + (i % 5); // 4 ? 8 h + const payableHours = status === LeaveApprovalStatus.APPROVED ? Math.min(requestedHours, 8) : null; rows.push({ employee_id: emp.id, bank_code_id: bc.id, leave_type: type, - start_date_time: start, - end_date_time: end, // ok: Date | null - comment: `Future leave #${i + 1}`, + date, + comment: `Future leave #${i + 1} (${bc.type})`, approval_status: status, + requested_hours: requestedHours, + payable_hours: payableHours, }); } @@ -75,7 +76,7 @@ async function main() { await prisma.leaveRequests.createMany({ data: rows }); } - console.log(`✓ LeaveRequests (future): ${rows.length} rows`); + console.log(`? LeaveRequests (future): ${rows.length} rows`); } -main().finally(() => prisma.$disconnect()); +main().finally(() => prisma.$disconnect()); \ No newline at end of file diff --git a/prisma/mock-seeds-scripts/08-leave-requests-archive.ts b/prisma/mock-seeds-scripts/08-leave-requests-archive.ts index d92a51f..45b1d43 100644 --- a/prisma/mock-seeds-scripts/08-leave-requests-archive.ts +++ b/prisma/mock-seeds-scripts/08-leave-requests-archive.ts @@ -1,7 +1,7 @@ -import { PrismaClient, LeaveApprovalStatus, LeaveRequests } from '@prisma/client'; +import { PrismaClient, LeaveApprovalStatus, LeaveTypes } from '@prisma/client'; if (process.env.SKIP_LEAVE_REQUESTS === 'true') { - console.log("⏭ Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)"); + console.log('?? Seed leave-requests ignor (SKIP_LEAVE_REQUESTS=true)'); process.exit(0); } @@ -15,65 +15,73 @@ function daysAgo(n: number) { } async function main() { - // 1) Récupère tous les employés const employees = await prisma.employees.findMany({ select: { id: true } }); if (!employees.length) { - throw new Error('Aucun employé trouvé. Exécute le seed employees avant celui-ci.'); + throw new Error('Aucun employ trouv. Excute le seed employees avant celui-ci.'); } - // 2) Va chercher les bank codes dont le type est SICK, VACATION ou HOLIDAY const leaveCodes = await prisma.bankCodes.findMany({ - where: { type: { in: ['SICK', 'VACATION'] } }, - select: { id: true, type: true, bank_code: true }, + where: { type: { in: ['SICK', 'VACATION', 'HOLIDAY'] } }, + select: { id: true, type: true }, }); if (!leaveCodes.length) { - throw new Error("Aucun bank code trouvé avec type in ('SICK','VACATION','HOLIDAY'). Vérifie ta table bank_codes."); + throw new Error("Aucun bank code trouv avec type in ('SICK','VACATION','HOLIDAY'). Vrifie ta table bank_codes."); } const statuses = Object.values(LeaveApprovalStatus); - const created: LeaveRequests[] = []; + const created = [] as Array<{ id: number; employee_id: number; leave_type: LeaveTypes; date: Date; comment: string; approval_status: LeaveApprovalStatus; requested_hours: number; payable_hours: number | null }>; - // 3) Crée quelques leave requests const COUNT = 12; for (let i = 0; i < COUNT; i++) { const emp = employees[i % employees.length]; const leaveCode = leaveCodes[Math.floor(Math.random() * leaveCodes.length)]; - const start = daysAgo(120 - i * 3); - const end = Math.random() < 0.6 ? daysAgo(119 - i * 3) : null; + const date = daysAgo(120 - i * 3); + const status = statuses[(i + 2) % statuses.length]; + const requestedHours = 4 + (i % 5); // 4 ? 8 h + const payableHours = status === LeaveApprovalStatus.APPROVED ? Math.min(requestedHours, 8) : null; const lr = await prisma.leaveRequests.create({ data: { employee_id: emp.id, bank_code_id: leaveCode.id, - // on stocke le "type" tel qu’il est défini dans bank_codes - leave_type: leaveCode.type as any, - start_date_time: start, - end_date_time: end, + leave_type: leaveCode.type as LeaveTypes, + date, comment: `Past leave #${i + 1} (${leaveCode.type})`, - approval_status: statuses[(i + 2) % statuses.length], + approval_status: status, + requested_hours: requestedHours, + payable_hours: payableHours, }, }); - created.push(lr); + created.push({ + id: lr.id, + employee_id: lr.employee_id, + leave_type: lr.leave_type, + date: lr.date, + comment: lr.comment, + approval_status: lr.approval_status, + requested_hours: requestedHours, + payable_hours: payableHours, + }); } - // 4) Archive for (const lr of created) { await prisma.leaveRequestsArchive.create({ data: { leave_request_id: lr.id, employee_id: lr.employee_id, leave_type: lr.leave_type, - start_date_time: lr.start_date_time, - end_date_time: lr.end_date_time, + date: lr.date, comment: lr.comment, approval_status: lr.approval_status, + requested_hours: lr.requested_hours, + payable_hours: lr.payable_hours, }, }); } - console.log(`✓ LeaveRequestsArchive: ${created.length} rows`); + console.log(`? LeaveRequestsArchive: ${created.length} rows`); } -main().finally(() => prisma.$disconnect()); +main().finally(() => prisma.$disconnect()); \ No newline at end of file diff --git a/src/modules/archival/archival.module.ts b/src/modules/archival/archival.module.ts index 7a8b73a..03f1bf9 100644 --- a/src/modules/archival/archival.module.ts +++ b/src/modules/archival/archival.module.ts @@ -28,7 +28,7 @@ import { EmployeesModule } from "../employees/employees.module"; LeaveRequestsArchiveController, ShiftsArchiveController, TimesheetsArchiveController, - ] + ], }) export class ArchivalModule {} \ No newline at end of file diff --git a/src/modules/archival/controllers/leave-requests-archive.controller.ts b/src/modules/archival/controllers/leave-requests-archive.controller.ts index 89cb024..1c5e4be 100644 --- a/src/modules/archival/controllers/leave-requests-archive.controller.ts +++ b/src/modules/archival/controllers/leave-requests-archive.controller.ts @@ -1,33 +1,7 @@ -import { Get, Param, ParseIntPipe, NotFoundException, Controller, UseGuards } from "@nestjs/common"; -import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { LeaveRequestsArchive, Roles as RoleEnum } from "@prisma/client"; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { LeaveRequestViewDto } from "src/modules/leave-requests/dtos/leave-request.view.dto"; -import { LeaveRequestsService } from "src/modules/leave-requests/services/holiday-leave-requests.service"; +import { Controller } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; @ApiTags('LeaveRequests Archives') // @UseGuards() @Controller('archives/leaveRequests') -export class LeaveRequestsArchiveController { - constructor(private readonly leaveRequestsService: LeaveRequestsService) {} - - @Get() - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) - @ApiOperation({ summary: 'List of archived leaveRequests'}) - @ApiResponse({ status: 200, description: 'List of archived leaveRequests', isArray: true }) - async findAllArchived(): Promise { - return this.leaveRequestsService.findAllArchived(); - } - - @Get() - //@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) - @ApiOperation({ summary: 'Fetch leaveRequest in archives with its Id'}) - @ApiResponse({ status: 200, description: 'Archived leaveRequest found'}) - async findOneArchived(@Param('id', ParseIntPipe) id: number ): Promise { - try{ - return await this.leaveRequestsService.findOneArchived(id); - }catch { - throw new NotFoundException(`Archived leaveRequest #${id} not found`); - } - } -} \ No newline at end of file +export class LeaveRequestsArchiveController {} \ No newline at end of file diff --git a/src/modules/archival/services/archival.service.ts b/src/modules/archival/services/archival.service.ts index 07ab621..7dbf567 100644 --- a/src/modules/archival/services/archival.service.ts +++ b/src/modules/archival/services/archival.service.ts @@ -1,7 +1,6 @@ import { Injectable, Logger } from "@nestjs/common"; import { Cron } from "@nestjs/schedule"; import { ExpensesQueryService } from "src/modules/expenses/services/expenses-query.service"; -import { LeaveRequestsService } from "src/modules/leave-requests/services/holiday-leave-requests.service"; import { ShiftsQueryService } from "src/modules/shifts/services/shifts-query.service"; import { TimesheetsQueryService } from "src/modules/timesheets/services/timesheets-query.service"; @@ -13,7 +12,6 @@ export class ArchivalService { private readonly timesheetsService: TimesheetsQueryService, private readonly expensesService: ExpensesQueryService, private readonly shiftsService: ShiftsQueryService, - private readonly leaveRequestsService: LeaveRequestsService, ) {} @Cron('0 0 3 * * 1', {timeZone:'America/Toronto'}) // chaque premier lundi du mois à 03h00 @@ -31,7 +29,7 @@ export class ArchivalService { await this.timesheetsService.archiveOld(); await this.expensesService.archiveOld(); await this.shiftsService.archiveOld(); - await this.leaveRequestsService.archiveExpired(); + // await this.leaveRequestsService.archiveExpired(); this.logger.log('archivation process done'); } catch (err) { this.logger.error('an error occured during archivation process ', err); diff --git a/src/modules/expenses/services/expenses-command.service.ts b/src/modules/expenses/services/expenses-command.service.ts index 06e3895..ae8af6b 100644 --- a/src/modules/expenses/services/expenses-command.service.ts +++ b/src/modules/expenses/services/expenses-command.service.ts @@ -53,8 +53,8 @@ export class ExpensesCommandService extends BaseApprovalService { } //validate date format - const dateOnly = toDateOnlyUTC(date); - if(Number.isNaN(dateOnly.getTime())) { + const date_only = toDateOnlyUTC(date); + if(Number.isNaN(date_only.getTime())) { throw new BadRequestException('Invalid date format (expected: YYYY-MM-DD)'); } @@ -62,14 +62,14 @@ export class ExpensesCommandService extends BaseApprovalService { const employee_id = await this.resolveEmployeeIdByEmail(email); //make sure a timesheet existes - const timesheet_id = await this.ensureTimesheetForDate(employee_id, dateOnly); + const timesheet_id = await this.ensureTimesheetForDate(employee_id, date_only); return this.prisma.$transaction(async (tx) => { const loadDay = async (): Promise => { const rows = await tx.expenses.findMany({ where: { timesheet_id: timesheet_id, - date: dateOnly, + date: date_only, }, include: { bank_code: { @@ -160,7 +160,7 @@ export class ExpensesCommandService extends BaseApprovalService { return tx.expenses.findFirst({ where: { timesheet_id: timesheet_id, - date: dateOnly, + date: date_only, bank_code_id: norm.bank_code_id, amount: norm.amount, comment: norm.comment, @@ -191,7 +191,7 @@ export class ExpensesCommandService extends BaseApprovalService { await tx.expenses.create({ data: { timesheet_id: timesheet_id, - date: dateOnly, + date: date_only, bank_code_id: new_exp.bank_code_id, amount: new_exp.amount, mileage: new_exp.mileage, diff --git a/src/modules/expenses/types and interfaces/expenses.types.interfaces.ts b/src/modules/expenses/types and interfaces/expenses.types.interfaces.ts index 84a82dc..5f592a5 100644 --- a/src/modules/expenses/types and interfaces/expenses.types.interfaces.ts +++ b/src/modules/expenses/types and interfaces/expenses.types.interfaces.ts @@ -9,5 +9,6 @@ export interface ExpenseResponse { }; export type UpsertExpenseResult = { - expenses: ExpenseResponse[] + action: UpsertAction; + day: ExpenseResponse[] }; \ No newline at end of file diff --git a/src/modules/expenses/utils/expenses.utils.ts b/src/modules/expenses/utils/expenses.utils.ts index edd2c4a..87e2120 100644 --- a/src/modules/expenses/utils/expenses.utils.ts +++ b/src/modules/expenses/utils/expenses.utils.ts @@ -1,5 +1,5 @@ import { BadRequestException } from "@nestjs/common"; -import { DayExpenseResponse } from "../types and interfaces/expenses.types.interfaces"; +import { ExpenseResponse } from "../types and interfaces/expenses.types.interfaces"; import { Prisma } from "@prisma/client"; //uppercase and trim for validation @@ -55,7 +55,7 @@ export function mapDbExpenseToDayResponse(row: { comment: string; is_approved: boolean; bank_code?: { type?: string | null } | null; -}): DayExpenseResponse { +}): ExpenseResponse { const yyyyMmDd = row.date.toISOString().slice(0,10); const toNum = (value: any)=> (value == null ? 0 : Number(value)); return { diff --git a/src/modules/leave-requests/dtos/leave-request-view.dto.ts b/src/modules/leave-requests/dtos/leave-request-view.dto.ts index db36da7..7cf3f35 100644 --- a/src/modules/leave-requests/dtos/leave-request-view.dto.ts +++ b/src/modules/leave-requests/dtos/leave-request-view.dto.ts @@ -10,5 +10,5 @@ export class LeaveRequestViewDto { employee_full_name!: string; payable_hours?: number; requested_hours?: number; - action?: 'created' | 'updated' | 'deleted'; + action?: 'create' | 'update' | 'delete'; } \ No newline at end of file diff --git a/src/modules/leave-requests/leave-requests.module.ts b/src/modules/leave-requests/leave-requests.module.ts index 2249f21..394b6be 100644 --- a/src/modules/leave-requests/leave-requests.module.ts +++ b/src/modules/leave-requests/leave-requests.module.ts @@ -4,6 +4,7 @@ import { LeaveRequestController } from "./controllers/leave-requests.controller" import { HolidayLeaveRequestsService } from "./services/holiday-leave-requests.service"; import { Module } from "@nestjs/common"; import { BusinessLogicsModule } from "src/modules/business-logics/business-logics.module"; +import { ShiftsCommandService } from "../shifts/services/shifts-command.service"; @Module({ imports: [BusinessLogicsModule], @@ -12,6 +13,7 @@ import { BusinessLogicsModule } from "src/modules/business-logics/business-logic HolidayService, HolidayLeaveRequestsService, PrismaService, + ShiftsCommandService, ], exports: [HolidayLeaveRequestsService], }) diff --git a/src/modules/leave-requests/mappers/leave-requests.mapper.ts b/src/modules/leave-requests/mappers/leave-requests.mapper.ts index fda4f6d..e93f94b 100644 --- a/src/modules/leave-requests/mappers/leave-requests.mapper.ts +++ b/src/modules/leave-requests/mappers/leave-requests.mapper.ts @@ -6,13 +6,13 @@ const toNum = (value?: Prisma.Decimal | null) => value !== null && value !== undefined ? Number(value) : undefined; export function mapRowToView(row: LeaveRequestRow): LeaveRequestViewDto { - const isoDate = row.date?.toISOString().slice(0, 10); - if (!isoDate) throw new Error(`Leave request #${row.id} has no date set.`); + const iso_date = row.date?.toISOString().slice(0, 10); + if (!iso_date) throw new Error(`Leave request #${row.id} has no date set.`); return { id: row.id, leave_type: row.leave_type, - date: isoDate, + date: iso_date, payable_hours: toNum(row.payable_hours), requested_hours: toNum(row.requested_hours), comment: row.comment, diff --git a/src/modules/leave-requests/services/holiday-leave-requests.service.ts b/src/modules/leave-requests/services/holiday-leave-requests.service.ts index 3e5fbcb..c3f614d 100644 --- a/src/modules/leave-requests/services/holiday-leave-requests.service.ts +++ b/src/modules/leave-requests/services/holiday-leave-requests.service.ts @@ -3,16 +3,14 @@ import { Injectable, NotFoundException, } from '@nestjs/common'; -import { LeaveApprovalStatus, LeaveTypes } from '@prisma/client'; - -import { HolidayService } from 'src/modules/business-logics/services/holiday.service'; -import { ShiftsCommandService } from 'src/modules/shifts/services/shifts-command.service'; -import { PrismaService } from 'src/prisma/prisma.service'; - -import { LeaveRequestViewDto } from '../dtos/leave-request-view.dto'; +import { LeaveApprovalStatus, LeaveTypes } from '@prisma/client'; import { HolidayUpsertAction, UpsertHolidayDto } from '../dtos/upsert-holiday.dto'; -import { mapRowToView } from '../mappers/leave-requests.mapper'; -import { leaveRequestsSelect } from '../utils/leave-requests.select'; +import { HolidayService } from 'src/modules/business-logics/services/holiday.service'; +import { ShiftsCommandService } from 'src/modules/shifts/services/shifts-command.service'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { LeaveRequestViewDto } from '../dtos/leave-request-view.dto'; +import { mapRowToView } from '../mappers/leave-requests.mapper'; +import { leaveRequestsSelect } from '../utils/leave-requests.select'; interface HolidayUpsertResult { action: HolidayUpsertAction; @@ -204,7 +202,7 @@ export class HolidayLeaveRequestsService { where: { id: { in: rows.map((row) => row.id) } }, }); - const deleted = rows.map((row) => ({ ...mapRowToView(row), action: 'delete' })); + const deleted = rows.map((row) => ({ ...mapRowToView(row), action: 'delete' as const})); return { action: 'delete', leave_requests: deleted }; } @@ -320,4 +318,4 @@ const toDateOnly = (iso: string): Date => { const toISODateKey = (date: Date): string => date.toISOString().slice(0, 10); const normalizeDates = (dates: string[]): string[] => - Array.from(new Set(dates.map((iso) => toISODateKey(toDateOnly(iso))))); \ No newline at end of file + Array.from(new Set(dates.map((iso) => toISODateKey(toDateOnly(iso))))); diff --git a/src/modules/leave-requests/utils/leave-request.transform.ts b/src/modules/leave-requests/utils/leave-request.transform.ts index b70c66d..63b9936 100644 --- a/src/modules/leave-requests/utils/leave-request.transform.ts +++ b/src/modules/leave-requests/utils/leave-request.transform.ts @@ -1,32 +1,19 @@ -import { LeaveRequestViewDto } from "../dtos/leave-request.view.dto"; -import { mapArchiveRowToView } from "../mappers/leave-requests-archive.mapper"; -import { mapRowToView } from "../mappers/leave-requests.mapper"; -import { LeaveRequestArchiveRow } from "./leave-requests-archive.select"; -import { LeaveRequestRow } from "./leave-requests.select"; +import { LeaveRequestViewDto } from '../dtos/leave-request-view.dto'; +import { mapArchiveRowToView } from '../mappers/leave-requests-archive.mapper'; +import { mapRowToView } from '../mappers/leave-requests.mapper'; +import { LeaveRequestArchiveRow } from './leave-requests-archive.select'; +import { LeaveRequestRow } from './leave-requests.select'; -function toUTCDateOnly(date: Date): Date { - return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())); -} - -const MS_PER_DAY = 86_400_000; -function computeDaysRequested(start_date: Date, end_date?: Date | null): number { - const start = toUTCDateOnly(start_date); - const end = toUTCDateOnly(end_date ?? start_date); - const diff = Math.floor((end.getTime() - start.getTime()) / MS_PER_DAY) + 1; - return Math.max(1, diff); -} - -/** Active (table leave_requests) : map + days_requested */ +/** Active (table leave_requests) : proxy to base mapper */ export function mapRowToViewWithDays(row: LeaveRequestRow): LeaveRequestViewDto { - const view = mapRowToView(row); - view.days_requested = computeDaysRequested(row.start_date_time, row.end_date_time); - return view; + return mapRowToView(row); } -/** Archive (table leave_requests_archive) : map + days_requested */ -export function mapArchiveRowToViewWithDays(row: LeaveRequestArchiveRow, email: string, employee_full_name?: string): - LeaveRequestViewDto { - const view = mapArchiveRowToView(row, email, employee_full_name!); - view.days_requested = computeDaysRequested(row.start_date_time, row.end_date_time); - return view; +/** 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