From 3237ae9358305a15f7972635714927194799041b Mon Sep 17 00:00:00 2001 From: Daniel Hurtado <81983296+DHurtado714-itesm@users.noreply.github.com> Date: Fri, 24 May 2024 12:49:21 -0600 Subject: [PATCH] Revert "Feat/Expenses/RF32: Endpoint for expenseReport data by id" --- .../migration.sql | 22 -- prisma/schema.prisma | 16 +- src/api/controllers/expense.controller.ts | 30 --- src/api/routes/expense.routes.ts | 11 - src/api/routes/index.routes.ts | 4 - .../__tests__/expense.service.test.ts | 77 ------- src/core/app/services/expense.service.ts | 45 ---- src/core/domain/entities/expense.entity.ts | 215 ------------------ .../expense-entity-from-db-model.mapper.ts | 33 --- .../infra/repositories/expense.repository.ts | 35 --- src/utils/enums/index.ts | 7 - 11 files changed, 13 insertions(+), 482 deletions(-) delete mode 100644 prisma/migrations/20240522045959_expenses_update_fields/migration.sql delete mode 100644 src/api/controllers/expense.controller.ts delete mode 100644 src/api/routes/expense.routes.ts delete mode 100644 src/core/app/services/__tests__/expense.service.test.ts delete mode 100644 src/core/app/services/expense.service.ts delete mode 100644 src/core/domain/entities/expense.entity.ts delete mode 100644 src/core/infra/mappers/expense-entity-from-db-model.mapper.ts delete mode 100644 src/core/infra/repositories/expense.repository.ts diff --git a/prisma/migrations/20240522045959_expenses_update_fields/migration.sql b/prisma/migrations/20240522045959_expenses_update_fields/migration.sql deleted file mode 100644 index a928729b..00000000 --- a/prisma/migrations/20240522045959_expenses_update_fields/migration.sql +++ /dev/null @@ -1,22 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `id_file` on the `expense` table. All the data in the column will be lost. - - You are about to drop the column `total_amount` on the `expense_report` table. All the data in the column will be lost. - - You are about to drop the `file` table. If the table is not empty, all the data it contains will be lost. - - Added the required column `title` to the `expense_report` table without a default value. This is not possible if the table is not empty. - -*/ --- DropForeignKey -ALTER TABLE "expense" DROP CONSTRAINT "expense_id_file_fkey"; - --- AlterTable -ALTER TABLE "expense" DROP COLUMN "id_file", -ADD COLUMN "url_file" VARCHAR(512); - --- AlterTable -ALTER TABLE "expense_report" DROP COLUMN "total_amount", -ADD COLUMN "title" VARCHAR(70) NOT NULL; - --- DropTable -DROP TABLE "file"; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index cc36dc36..7ede249a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,6 +1,5 @@ generator client { provider = "prisma-client-js" - previewFeatures = ["omitApi"] } datasource db { @@ -105,17 +104,18 @@ model expense { created_at DateTime @default(now()) @db.Timestamp(6) updated_at DateTime? @updatedAt @db.Timestamp(6) id_report String @db.Uuid - url_file String? @db.VarChar(512) + id_file String? @db.Uuid + file file? @relation(fields: [id_file], references: [id], onDelete: NoAction, onUpdate: NoAction) expense_report expense_report @relation(fields: [id_report], references: [id], onDelete: NoAction, onUpdate: NoAction) } model expense_report { id String @id @db.Uuid - title String @db.VarChar(70) description String @db.VarChar(255) start_date DateTime @db.Date end_date DateTime? @db.Date status String? @db.VarChar(256) + total_amount Decimal? @db.Decimal(8, 2) created_at DateTime @default(now()) @db.Timestamp(6) updated_at DateTime? @updatedAt @db.Timestamp(6) id_employee String @db.Uuid @@ -123,6 +123,16 @@ model expense_report { employee employee @relation(fields: [id_employee], references: [id], onDelete: NoAction, onUpdate: NoAction) } +model file { + id String @id @db.Uuid + description String? @db.VarChar(256) + format String @default(".zip") @db.VarChar(256) + url String @unique @db.VarChar(256) + created_at DateTime @default(now()) @db.Timestamp(6) + updated_at DateTime? @updatedAt @db.Timestamp(6) + expense expense[] +} + model form { id String @id @db.Uuid title String @db.VarChar(70) diff --git a/src/api/controllers/expense.controller.ts b/src/api/controllers/expense.controller.ts deleted file mode 100644 index c45d8e07..00000000 --- a/src/api/controllers/expense.controller.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Request, Response } from 'express'; -import { z } from 'zod'; -import { ExpenseService } from '../../core/app/services/expense.service'; - -const idSchema = z.object({ - id: z.string().uuid(), -}); - -/** - * A function that handles the request to obtain expense report details by its id - * @param req HTTP Request - * @param res Server response - */ -async function getReportById(req: Request, res: Response) { - try { - const { id } = idSchema.parse({ id: req.params.id }); - const expenseDetails = await ExpenseService.getReportById(id, req.body.auth.email); - if (expenseDetails) { - res.status(200).json(expenseDetails); - } - } catch (error: any) { - if (error.message === 'Unauthorized employee') { - res.status(403).json({ message: error.message }); - } else { - res.status(500).json({ message: error.message }); - } - } -} - -export const ExpenseController = { getReportById }; diff --git a/src/api/routes/expense.routes.ts b/src/api/routes/expense.routes.ts deleted file mode 100644 index 7031f7a6..00000000 --- a/src/api/routes/expense.routes.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Router } from 'express'; -import { SupportedRoles } from '../../utils/enums'; -import { ExpenseController } from '../controllers/expense.controller'; -import { checkAuthRole } from '../middlewares/rbac.middleware'; - -const router = Router(); - -router.use(checkAuthRole([SupportedRoles.ACCOUNTING, SupportedRoles.LEGAL, SupportedRoles.ADMIN])); -router.get('/report/:id', ExpenseController.getReportById); - -export { router as ExpenseRouter }; diff --git a/src/api/routes/index.routes.ts b/src/api/routes/index.routes.ts index 3b932623..52a8b38d 100644 --- a/src/api/routes/index.routes.ts +++ b/src/api/routes/index.routes.ts @@ -4,7 +4,6 @@ import { AdminRouter } from './admin.routes'; import { CompanyRouter } from './company.routes'; import { DepartmentRouter } from './department.routes'; import { EmployeeRouter } from './employee.routes'; -import { ExpenseRouter } from './expense.routes'; import { HomeRouter } from './home.routes'; import { NotificationRouter } from './notification.routes'; import { ProjectRouter } from './project.routes'; @@ -40,9 +39,6 @@ baseRouter.use(`${V1_PATH}/company`, CompanyRouter); // Notification baseRouter.use(`${V1_PATH}/notification`, NotificationRouter); -// Expense -baseRouter.use(`${V1_PATH}/expense`, ExpenseRouter); - // Health check baseRouter.use(`${V1_PATH}/health`, (_req, res) => res.send('OK')); diff --git a/src/core/app/services/__tests__/expense.service.test.ts b/src/core/app/services/__tests__/expense.service.test.ts deleted file mode 100644 index 8d7809f5..00000000 --- a/src/core/app/services/__tests__/expense.service.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { faker } from '@faker-js/faker'; -import { expect } from 'chai'; -import { randomUUID } from 'crypto'; -import { default as Sinon, default as sinon } from 'sinon'; -import { ExpenseReportStatus, SupportedRoles } from '../../../../utils/enums'; - -import { EmployeeRepository } from '../../../infra/repositories/employee.repository'; -import { ExpenseRepository } from '../../../infra/repositories/expense.repository'; -import { RoleRepository } from '../../../infra/repositories/role.repository'; -import { ExpenseService } from '../expense.service'; - -describe('ExpenseService', () => { - let findEmployeeByEmailStub: Sinon.SinonStub; - let findRoleByEmailStub: Sinon.SinonStub; - let findExpenseByIdStub: Sinon.SinonStub; - - beforeEach(() => { - findEmployeeByEmailStub = sinon.stub(EmployeeRepository, 'findByEmail'); - findRoleByEmailStub = sinon.stub(RoleRepository, 'findByEmail'); - findExpenseByIdStub = sinon.stub(ExpenseRepository, 'findById'); - }); - - afterEach(() => { - sinon.restore(); - }); - - describe('getReportById', () => { - it('Should return the expense report details', async () => { - const reportId = randomUUID(); - const userEmail = faker.internet.email(); - const userId = randomUUID(); - - const employee = { - id: userId, - email: userEmail, - name: faker.lorem.words(2), - role: SupportedRoles.ADMIN, - }; - const role = { - title: SupportedRoles.ADMIN, - }; - const expenses = [ - { - id: randomUUID(), - title: faker.lorem.words(3), - justification: faker.lorem.words(10), - totalAmount: faker.number.float(), - date: new Date(), - createdAt: new Date(), - idReport: reportId, - }, - ]; - const existingReport = { - id: reportId, - title: faker.lorem.words(3), - description: faker.lorem.words(10), - startDate: new Date(), - endDate: new Date(), - status: ExpenseReportStatus.PENDING, - createdAt: new Date(), - idEmployee: userId, - expenses: expenses, - }; - - findEmployeeByEmailStub.resolves(employee); - findRoleByEmailStub.resolves(role); - findExpenseByIdStub.resolves(existingReport); - - const res = await ExpenseService.getReportById(reportId, userEmail); - - expect(res).to.exist; - expect(res).to.be.equal(existingReport); - expect(res.id).to.equal(reportId); - expect(res.expenses?.length).to.equal(expenses.length); - }); - }); -}); diff --git a/src/core/app/services/expense.service.ts b/src/core/app/services/expense.service.ts deleted file mode 100644 index 9c51c4c9..00000000 --- a/src/core/app/services/expense.service.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Decimal } from '@prisma/client/runtime/library'; -import { SupportedRoles } from '../../../utils/enums'; -import { ExpenseReport } from '../../domain/entities/expense.entity'; -import { EmployeeRepository } from '../../infra/repositories/employee.repository'; -import { ExpenseRepository } from '../../infra/repositories/expense.repository'; -import { RoleRepository } from '../../infra/repositories/role.repository'; - -/** - * - * @param getReportById the id of the expense report we want the details - * @param email the email of the user - * @returns {Promise} a promise that resolves the details of the expense report - * @throws {Error} if an unexpected error occurs - */ - -async function getReportById(reportId: string, email: string): Promise { - try { - const [employee, role, expenseReport] = await Promise.all([ - EmployeeRepository.findByEmail(email), - RoleRepository.findByEmail(email), - ExpenseRepository.findById(reportId), - ]); - - if (role.title.toUpperCase() != SupportedRoles.ADMIN.toUpperCase() && expenseReport.idEmployee != employee?.id) { - throw new Error('Unauthorized employee'); - } - - let totalAmount = new Decimal(0); - if (expenseReport.expenses) { - expenseReport.expenses.forEach(expense => { - totalAmount = totalAmount.add(expense.totalAmount); - }); - } - expenseReport.totalAmount = totalAmount; - - return expenseReport; - } catch (error: any) { - if (error.message === 'Unauthorized employee') { - throw error; - } - throw new Error('An unexpected error occured'); - } -} - -export const ExpenseService = { getReportById }; diff --git a/src/core/domain/entities/expense.entity.ts b/src/core/domain/entities/expense.entity.ts deleted file mode 100644 index 5b4241e7..00000000 --- a/src/core/domain/entities/expense.entity.ts +++ /dev/null @@ -1,215 +0,0 @@ -import { employee, expense } from '@prisma/client'; -import { Decimal } from '@prisma/client/runtime/library'; - -/** - * @brief This class is used to define the structure of the Expense entity - * - * @param id: string - Unique identifier of the expense - * @param title: string - Expense title - * @param justification: string - Expense justification - * @param totalAmount: Decimal - Expense amount - + @param status?: string - Expense status (optional) - * @param category?: string - Expense category (optional) - * @param date: Date - Expense date - * @param createdAt: Date - Expense creation date - * @param updatedAt?: Date - Expense update date (optional) - * @param idReport: string - Unique identifier of expense report associated - * @param urlFile?: string - URL of the file associated with the expense (optional) - * - * @return void - * - * @description The structure is based on the MER, and there's the idea of using custom data types, like UUID. - */ - -export interface ExpenseEntity { - /** - * @param id: string - Expense id - */ - id: string; - /** - * @param title: string - Expense title - */ - title: string; - /** - * @param justification: string - Expense justification - */ - justification: string; - /** - * @param totalAmount: Decimal - Expense amount - */ - totalAmount: Decimal; - /** - * @param status: string - Expense status - */ - status?: string | null; - /** - * @param category: string - Expense category (optional) - */ - category?: string | null; - /** - * @param date: Date - Expense date - */ - date: Date; - /** - * @param createdAt: Date - Expense creation date - */ - createdAt: Date; - /** - * @param updatedAt: Date - Expense update date (optional) - */ - updatedAt?: Date | null; - /** - * @param idReport: string - Expense report id - */ - idReport: string; - /** - * @param urlFile: string - URL of the file associated with the expense (optional) - */ - urlFile?: string | null; -} - -/** - * @brief This class is used to define the structure of the Expense Report entity - * - * @param id: string - Unique identifier of the expense report - * @param title: string - Expense Report title - * @param description: string - Expense Report description - * @param startDate: Date - Expense Report start date - * @param endDate?: Date - Expense Report end date (optional) - + @param status?: string - Expense Report status (optional) - * @param createdAt?: Date - Expense Report creation date (optional) - * @param updatedAt?: Date - Expense Report update date (optional) - * @param idEmployee: string - Unique identifier of the employee associated - * @param employeeFirstName?: string - Employee first name (optional) - * @param employeeLastName?: string - Employee last name (optional) - * @param expenses?: ExpenseEntity[] - Array of expenses associated with the report (optional) - * @param totalAmount?: Decimal - Total amount of the expenses associated with the report (optional) - * - * @return void - * - * @description The structure is based on the MER, and there's the idea of using custom data types, like UUID. - */ - -export interface ExpenseReport { - /** - * @param id: string - Expense report id - */ - id: string; - /** - * @param title: string - Expense report title - */ - title: string; - /** - * @param description: string - Expense report description - */ - description: string; - /** - * @param startDate: Date - Expense report start date - */ - startDate: Date; - /** - * @param endDate: Date - Expense report end date - */ - endDate?: Date | null; - /** - * @param status: string - Expense report status - */ - status?: string | null; - /** - * @param createdAt: Date - Expense report creation date - */ - createdAt?: Date | null; - /** - * @param updatedAt: Date - Expense report update date - */ - updatedAt?: Date | null; - /** - * @param idEmployee: string - Employee id - */ - idEmployee: string; - /** - * @param employeeFirstName: string - Employee first name - */ - employeeFirstName?: string; - /** - * @param employeeLastName: string - Employee last name - */ - employeeLastName?: string; - /** - * @param expenses: ExpenseEntity[] - Array of expenses associated with the report - */ - expenses?: ExpenseEntity[]; - - /** - * @param totalAmount: Decimal - Total amount of the expenses associated with the report - */ - totalAmount?: Decimal; -} - -/** - * @brief This class is used to define the structure of the Expense Report Raw Data from the db. - * - * @param id: string - Unique identifier of the expense report - * @param title: string - Expense Report title - * @param description: string - Expense Report description - * @param start_date: Date - Expense Report start date - * @param end_date?: Date - Expense Report end date (optional) - + @param status?: string - Expense Report status (optional) - * @param createdAt: Date - Expense Report creation date - * @param updatedAt?: Date - Expense Report update date (optional) - * @param id_employee: string - Unique identifier of the employee associated - * @param employee?: employee - Employee information associated with the report (optional) - * @param expense?: expense[] - Array of expenses associated with the report (optional) - * @param totalAmount?: Decimal - Total amount of the expenses associated with the report (optional) - * - * @return void - * - * @description The structure is based on the MER, and there's the idea of using custom data types, like UUID. - */ - -export interface RawExpenseReport { - /** - * @param id: string - Expense report id - */ - id: string; - /** - * @param title: string - Expense report title - */ - title: string; - /** - * @param description: string - Expense report description - */ - description: string; - /** - * @param startDate: Date - Expense report start date - */ - start_date: Date; - /** - * @param endDate: Date - Expense report end date - */ - end_date?: Date | null; - /** - * @param status: string - Expense report status - */ - status?: string | null; - /** - * @param createdAt: Date - Expense report creation date - */ - createdAt?: Date | null; - /** - * @param updatedAt: Date - Expense report update date - */ - updatedAt?: Date | null; - /** - * @param idEmployee: string - Employee id - */ - id_employee: string; - /** - * @param employee: employee - Employee information associated with the report - */ - employee?: employee | null; - /** - * @param expense: expense[] - Array of expenses associated with the report - */ - expense?: expense[] | null; -} diff --git a/src/core/infra/mappers/expense-entity-from-db-model.mapper.ts b/src/core/infra/mappers/expense-entity-from-db-model.mapper.ts deleted file mode 100644 index b7970f9b..00000000 --- a/src/core/infra/mappers/expense-entity-from-db-model.mapper.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { expense } from '@prisma/client'; -import { ExpenseEntity, ExpenseReport, RawExpenseReport } from '../../domain/entities/expense.entity'; - -export function mapExpenseEntityFromDbModel(model: expense): ExpenseEntity { - return { - id: model.id, - title: model.title, - justification: model.justification, - totalAmount: model.total_amount, - status: model.status ? model.status : '', - category: model.category ? model.category : '', - date: model.date, - createdAt: model.created_at, - updatedAt: model.updated_at ? model.updated_at : undefined, - idReport: model.id_report, - urlFile: model.url_file ? model.url_file : '', - }; -} - -export function mapExpenseReportEntityFromDbModel(model: RawExpenseReport): ExpenseReport { - return { - id: model.id, - title: model.title, - description: model.description, - startDate: model.start_date, - endDate: model.end_date ? model.end_date : undefined, - status: model.status ? model.status : undefined, - idEmployee: model.id_employee, - employeeFirstName: model.employee?.first_name ? model.employee.first_name : '', - employeeLastName: model.employee?.last_name ? model.employee.last_name : '', - expenses: model.expense ? model.expense.map(mapExpenseEntityFromDbModel) : [], - }; -} diff --git a/src/core/infra/repositories/expense.repository.ts b/src/core/infra/repositories/expense.repository.ts deleted file mode 100644 index 386489c4..00000000 --- a/src/core/infra/repositories/expense.repository.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Prisma } from '../../..'; -import { ExpenseReport } from '../../domain/entities/expense.entity'; -import { NotFoundError } from '../../errors/not-found.error'; -import { mapExpenseReportEntityFromDbModel } from '../mappers/expense-entity-from-db-model.mapper'; - -const RESOURCE_NAME = 'Expense report'; - -/** - * Finds a expense report by id - * @version 1.0.0 - * @returns {Promise} a promise that resolves in a expense report entity. - */ -async function findById(id: string): Promise { - try { - const data = await Prisma.expense_report.findUnique({ - where: { - id: id, - }, - include: { - expense: true, - employee: true, - }, - }); - - if (!data) { - throw new NotFoundError(RESOURCE_NAME); - } - - return mapExpenseReportEntityFromDbModel(data); - } catch (error: unknown) { - throw new Error(`${RESOURCE_NAME} repository error`); - } -} - -export const ExpenseRepository = { findById }; diff --git a/src/utils/enums/index.ts b/src/utils/enums/index.ts index 540574b7..e402f2f0 100644 --- a/src/utils/enums/index.ts +++ b/src/utils/enums/index.ts @@ -57,10 +57,3 @@ export enum ProjectPeriodicity { TWELVE_MONTHS = '12 months', WHEN_NEEDED = 'When needed', } - -export enum ExpenseReportStatus { - ACCEPTED = 'Accepted', - PAYED = 'Payed', - PENDING = 'Pending', - REJECTED = 'Rejected', -}