fix(expenses): fixes to dto

This commit is contained in:
Matthieu Haineault 2025-11-17 15:27:46 -05:00
parent e43cb489cf
commit 6e52bdb4e4
6 changed files with 37 additions and 24 deletions

View File

@ -12,14 +12,14 @@ export class ExpenseController {
constructor(private readonly upsert_service: ExpenseUpsertService) { }
@Post('create')
create(@Req() req, @Body() dto: ExpenseDto): Promise<Result<GetExpenseDto, string>> {
create(@Req() req, @Body() dto: ExpenseDto): Promise<Result<ExpenseDto, string>> {
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<Result<GetExpenseDto, string>> {
update(@Body() dto: ExpenseDto): Promise<Result<ExpenseDto, string>> {
return this.upsert_service.updateExpense(dto);
}

View File

@ -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
}

View File

@ -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 {}

View File

@ -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<Result<GetExpenseDto, string>> {
async createExpense(dto: ExpenseDto, email: string): Promise<Result<ExpenseDto, string>> {
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<Result<GetExpenseDto, string>> {
async updateExpense(dto: ExpenseDto): Promise<Result<ExpenseDto, string>> {
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(),

View File

@ -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 ?? '',

View File

@ -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 = {