import { weekStartSunday, toStringFromDate, toDateFromString } from "src/common/utils/date-utils"; import { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; import { expense_select, timesheet_select } from "src/time-and-attendance/utils/selects.utils"; import { PrismaPostgresService } from "prisma/postgres/prisma-postgres.service"; import { Injectable } from "@nestjs/common"; import { Result } from "src/common/errors/result-error.factory"; import { BankCodesResolver } from "src/common/mappers/bank-type-id.mapper"; import { ExpenseDto } from "src/time-and-attendance/expenses/expense-create.dto"; import { normalizeAndParseExpenseDto } from "src/time-and-attendance/expenses/expense.utils"; import { PayPeriodEventService } from "src/time-and-attendance/pay-period/services/pay-period-event.service"; @Injectable() export class ExpenseUpdateService { constructor( private readonly prisma: PrismaPostgresService, private readonly emailResolver: EmailToIdResolver, private readonly typeResolver: BankCodesResolver, private readonly payPeriodEventService: PayPeriodEventService, ) { } //_________________________________________________________________ // UPDATE //_________________________________________________________________ async updateExpense(dto: ExpenseDto, email: string, employee_email?: string): Promise> { try { const account_email = employee_email ?? email; //fetch employee_id using req.user.email const employee_id = await this.emailResolver.findIdByEmail(account_email); if (!employee_id.success) return { success: false, error: employee_id.error }; //normalize string , date format and parse numbers const normed_expense = await normalizeAndParseExpenseDto(dto); if (!normed_expense.success) return { success: false, error: normed_expense.error } const type = await this.typeResolver.findBankCodeIDByType(dto.type); if (!type.success) return { success: false, error: 'INVALID_EXPENSE_TYPE' } //added timesheet_id modification check according to the new date const new_timesheet_start_date = weekStartSunday(toDateFromString(dto.date)); const timesheet = await this.prisma.client.timesheets.findFirst({ where: { start_date: new_timesheet_start_date, employee_id: employee_id.data }, select: timesheet_select, }); if (!timesheet) return { success: false, error: `TIMESHEET_NOT_FOUND` } //checks for modifications const data = { ...normed_expense.data, bank_code_id: type.data, is_approved: dto.is_approved, }; if (!data) return { success: false, error: `INVALID_EXPENSE` } //push updates and get updated datas const expense = await this.prisma.client.expenses.update({ where: { id: dto.id, timesheet_id: timesheet.id }, data, select: expense_select, }); if (!expense) return { success: false, error: 'INVALID_EXPENSE' } //build an object to return to the frontend const updated: ExpenseDto = { ...expense, type: expense.bank_code.type, date: toStringFromDate(expense.date), amount: expense.amount?.toNumber(), mileage: expense.mileage?.toNumber(), attachment: expense.attachment ?? undefined, supervisor_comment: expense.supervisor_comment ?? undefined, }; // notify timesheet-approval observers of changes, but only if it came // from timesheet and not timesheet-approval (no employee_email) if (!employee_email) { this.payPeriodEventService.emit({ employee_email: email, event_type: 'expense', action: 'update', }); } return { success: true, data: updated }; } catch (error) { return { success: false, error: 'EXPENSE_NOT_FOUND' }; } } }