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 { EmailToIdResolver } from "src/common/mappers/email-id.mapper"; import { toStringFromDate, weekStartSunday } from "src/common/utils/date-utils"; import { PrismaService } from "prisma/postgres/prisma-postgres.service"; 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"; import { expense_select } from "src/time-and-attendance/utils/selects.utils"; @Injectable() export class ExpenseCreateService { constructor( private readonly prisma: PrismaService, private readonly emailResolver: EmailToIdResolver, private readonly typeResolver: BankCodesResolver, private readonly payPeriodEventService: PayPeriodEventService, ) { } //_________________________________________________________________ // CREATE //_________________________________________________________________ async createExpense(dto: ExpenseDto, email: string): Promise> { try { //fetch employee_id using req.user.email const employee_id = await this.emailResolver.findIdByEmail(email); if (!employee_id.success) return { success: false, error: employee_id.error }; //normalize strings and dates 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' } //finds the timesheet using expense.date by finding the sunday const start_date = weekStartSunday(normed_expense.data.date); const timesheet = await this.prisma.timesheets.findFirst({ where: { start_date, employee_id: employee_id.data }, select: { id: true, employee_id: true }, }); if (!timesheet) return { success: false, error: `TIMESHEET_NOT_FOUND` }; //create a new expense const expense = await this.prisma.expenses.create({ data: { ...normed_expense.data, bank_code_id: type.data, timesheet_id: timesheet.id, is_approved: dto.is_approved, }, //return the newly created expense with id select: expense_select, }); if (!expense) return { success: false, error: `INVALID_EXPENSE` }; //build an object to return to the frontend to display const created: ExpenseDto = { ...expense, type: dto.type, date: toStringFromDate(expense.date), amount: expense.amount?.toNumber() ?? undefined, mileage: expense.mileage?.toNumber(), attachment: expense.attachment ?? undefined, supervisor_comment: expense.supervisor_comment ?? undefined, }; // notify timesheet approval observers of changes this.payPeriodEventService.emit({ employee_email: email, event_type: 'expense', action: 'create', }); return { success: true, data: created }; } catch (error) { return { success: false, error: 'INVALID_EXPENSE' }; } } }