From 6e52bdb4e407c91ad28c817c8c37132771b45cfc Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 17 Nov 2025 15:27:46 -0500 Subject: [PATCH] fix(expenses): fixes to dto --- .../controllers/expense.controller.ts | 4 +-- .../expenses/dtos/expense-create.dto.ts | 22 ++++++++-------- .../expenses/expenses.module.ts | 3 ++- .../services/expense-upsert.service.ts | 26 ++++++++++++------- .../timesheet-get-overview.service.ts | 1 + .../utils/selects.utils.ts | 5 +++- 6 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/time-and-attendance/expenses/controllers/expense.controller.ts b/src/time-and-attendance/expenses/controllers/expense.controller.ts index 700fd50..3a6c138 100644 --- a/src/time-and-attendance/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/expenses/controllers/expense.controller.ts @@ -12,14 +12,14 @@ export class ExpenseController { constructor(private readonly upsert_service: ExpenseUpsertService) { } @Post('create') - create(@Req() req, @Body() dto: ExpenseDto): Promise> { + 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') - update(@Body() dto: ExpenseDto): Promise> { + update(@Body() dto: ExpenseDto): Promise> { return this.upsert_service.updateExpense(dto); } diff --git a/src/time-and-attendance/expenses/dtos/expense-create.dto.ts b/src/time-and-attendance/expenses/dtos/expense-create.dto.ts index 3f507d1..3f5c339 100644 --- a/src/time-and-attendance/expenses/dtos/expense-create.dto.ts +++ b/src/time-and-attendance/expenses/dtos/expense-create.dto.ts @@ -1,14 +1,14 @@ -import { IsBoolean, IsInt, IsOptional, IsString, MaxLength } from "class-validator"; +import { IsBoolean, IsDecimal, IsInt, IsOptional, IsString, MaxLength } from "class-validator"; export class ExpenseDto { - id: number; - type!: string; - timesheet_id!: number; - attachment?: number; - date!: string; - amount?: number; - mileage?: number; - comment!: string; - is_approved!: boolean; - supervisor_comment?: string + @IsInt() id: number; + @IsString() type: string; + @IsString() date: string; + @IsBoolean() is_approved: boolean; + @IsOptional() @IsDecimal() amount?: number; + @IsOptional() @IsDecimal() mileage?: number; + @IsOptional() @IsInt() attachment?: number; + @IsOptional() @IsInt() timesheet_id?: number; + @IsOptional() @IsString() @MaxLength(280) comment: string; + @IsOptional() @IsString() @MaxLength(280) supervisor_comment?: string } \ No newline at end of file diff --git a/src/time-and-attendance/expenses/expenses.module.ts b/src/time-and-attendance/expenses/expenses.module.ts index 3891c0c..fdeeeaf 100644 --- a/src/time-and-attendance/expenses/expenses.module.ts +++ b/src/time-and-attendance/expenses/expenses.module.ts @@ -3,10 +3,11 @@ import { ExpenseController } from "src/time-and-attendance/expenses/controllers/ import { Module } from "@nestjs/common"; import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; import { BankCodesResolver } from "src/common/mappers/bank-type-id.mapper"; +import { EmployeeTimesheetResolver } from "src/common/mappers/timesheet.mapper"; @Module({ controllers: [ ExpenseController ], - providers: [ ExpenseUpsertService, EmailToIdResolver, BankCodesResolver ], + providers: [ ExpenseUpsertService, EmailToIdResolver, BankCodesResolver, EmployeeTimesheetResolver ], }) export class ExpensesModule {} \ No newline at end of file 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 ee51f55..93a85a0 100644 --- a/src/time-and-attendance/expenses/services/expense-upsert.service.ts +++ b/src/time-and-attendance/expenses/services/expense-upsert.service.ts @@ -1,15 +1,14 @@ - import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; 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/expenses/dtos/expense-get.dto"; import { ExpenseEntity } from "src/time-and-attendance/expenses/dtos/expense-entity.dto"; -import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; import { Injectable } from "@nestjs/common"; import { Result } from "src/common/errors/result-error.factory"; import { NormalizedExpense } from "src/time-and-attendance/utils/type.utils"; import { weekStartSunday, toStringFromDate, toDateFromString } from "src/common/utils/date-utils"; import { BankCodesResolver } from "src/common/mappers/bank-type-id.mapper"; +import { EmployeeTimesheetResolver } from "src/common/mappers/timesheet.mapper"; +import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; @Injectable() @@ -18,12 +17,13 @@ export class ExpenseUpsertService { private readonly prisma: PrismaService, private readonly emailResolver: EmailToIdResolver, private readonly typeResolver: BankCodesResolver, + private readonly timesheetResolver: EmployeeTimesheetResolver, ) { } //_________________________________________________________________ // CREATE //_________________________________________________________________ - async createExpense(dto: ExpenseDto, email: string): Promise> { + async createExpense(dto: ExpenseDto, email: string): Promise> { try { //fetch employee_id using req.user.email const employee_id = await this.emailResolver.findIdByEmail(email); @@ -54,10 +54,11 @@ export class ExpenseUpsertService { if (!expense) return { success: false, error: `An error occured during creation. Expense is invalid` }; //build an object to return to the frontend to display - const created: GetExpenseDto = { + const created: ExpenseDto = { ...expense, + type: dto.type, date: toStringFromDate(expense.date), - amount: expense.amount?.toNumber(), + amount: expense.amount?.toNumber() ?? undefined, mileage: expense.mileage?.toNumber(), attachment: expense.attachment ?? undefined, supervisor_comment: expense.supervisor_comment ?? undefined, @@ -72,17 +73,23 @@ export class ExpenseUpsertService { //_________________________________________________________________ // UPDATE //_________________________________________________________________ - async updateExpense(dto: ExpenseDto): Promise> { + async updateExpense(dto: ExpenseDto): Promise> { try { //normalize string , date format and parse numbers const normed_expense = await this.normalizeAndParseExpenseDto(dto); if (!normed_expense.success) return { success: false, error: normed_expense.error } + const timesheet = await this.prisma.timesheets.findUnique({ + where: { id: dto.timesheet_id }, + select: expense_select, + }); + if (!timesheet) return { success: false, error: `Timesheet ${dto.timesheet_id} not found` } + //checks for modifications const data: ExpenseEntity = { ...normed_expense.data, id: dto.id, - timesheet_id: dto.timesheet_id, + timesheet_id: timesheet?.id, is_approved: dto.is_approved, }; if (!data) return { success: false, error: `An error occured during normalization. Expense with id: ${dto.id} is invalid` } @@ -96,8 +103,9 @@ export class ExpenseUpsertService { if (!expense) return { success: false, error: `An error occured during update. Expense with id: ${data.id} was not updated` } //build an object to return to the frontend - const updated: GetExpenseDto = { + const updated: ExpenseDto = { ...expense, + type: expense.bank_code.type, date: toStringFromDate(expense.date), amount: expense.amount?.toNumber(), mileage: expense.mileage?.toNumber(), 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 f9be0cd..9f5481a 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 @@ -160,6 +160,7 @@ export class GetTimesheetsOverviewService { amount: expense.amount != null ? Number(expense.amount) : undefined, mileage: expense.mileage != null ? Number(expense.mileage) : undefined, id: expense.id ?? null, + timesheet_id: expense.timesheet_id, attachment: expense.attachment_record ? String(expense.attachment_record.id) : undefined, is_approved: expense.is_approved ?? false, comment: expense.comment ?? '', diff --git a/src/time-and-attendance/utils/selects.utils.ts b/src/time-and-attendance/utils/selects.utils.ts index f81c0f4..2e40431 100644 --- a/src/time-and-attendance/utils/selects.utils.ts +++ b/src/time-and-attendance/utils/selects.utils.ts @@ -3,7 +3,9 @@ import { Prisma } from "@prisma/client"; export const expense_select = { id: true, timesheet_id: true, - bank_code_id: true, + bank_code: { + select: { type: true, id: true } + }, attachment: true, date: true, amount: true, @@ -11,6 +13,7 @@ export const expense_select = { comment: true, supervisor_comment: true, is_approved: true, + } satisfies Prisma.ExpensesSelect; export const shift_select = {