diff --git a/src/api/controllers/project.controller.ts b/src/api/controllers/project.controller.ts index 284c8695..d41eb4ff 100644 --- a/src/api/controllers/project.controller.ts +++ b/src/api/controllers/project.controller.ts @@ -48,8 +48,8 @@ async function createProject(req: Request, res: Response) { startDate: data.startDate, }); res.status(201).json(newProject); - } catch (error: unknown) { - res.status(400).json({ message: error }); + } catch (error: any) { + res.status(400).json({ message: error.message }); } } @@ -98,20 +98,6 @@ async function getProjectsClient(req: Request, res: Response) { } } -/** - * A function that calls the service to get all projects in the database. - * @param req HTTP Request - * @param res Server response - */ -async function getAllProjects(req: Request, res: Response) { - try { - const data = await ProjectService.getAllProjects(); - res.status(200).json({ data: data }); - } catch (error: any) { - res.status(500).json({ message: error.message }); - } -} - /** * Retrieves all projects from a certain department * @param req An HTTP Request @@ -177,7 +163,6 @@ async function updateProjectStatus(req: Request, res: Response) { export const ProjectController = { getReportData, createProject, - getAllProjects, getProjectsClient, getProjectById, updateProject, diff --git a/src/core/app/services/__tests__/company.service.test.ts b/src/core/app/services/__tests__/company.service.test.ts index d798de48..07e50bc5 100644 --- a/src/core/app/services/__tests__/company.service.test.ts +++ b/src/core/app/services/__tests__/company.service.test.ts @@ -3,23 +3,19 @@ import chai, { expect } from 'chai'; import chaiAsPromised from 'chai-as-promised'; import { randomUUID } from 'crypto'; import sinon from 'sinon'; -import { SupportedDepartments } from '../../../../utils/enums'; import { CompanyRepository } from '../../../infra/repositories/company.repository'; -import { ProjectRepository } from '../../../infra/repositories/project.repository'; import { CompanyService } from '../company.service'; chai.use(chaiAsPromised); describe('CompanyService', () => { let findAllCompaniesStub: sinon.SinonStub; - let findAllProjectsStub: sinon.SinonStub; let updateCompanyStub: sinon.SinonStub; let findCompanyByIdStub: sinon.SinonStub; let archiveClientdStub: sinon.SinonStub; let getArchivedStatusStub: sinon.SinonStub; beforeEach(() => { - findAllProjectsStub = sinon.stub(ProjectRepository, 'findAll'); findAllCompaniesStub = sinon.stub(CompanyRepository, 'findAll'); updateCompanyStub = sinon.stub(CompanyRepository, 'update'); findCompanyByIdStub = sinon.stub(CompanyRepository, 'findById'); @@ -33,7 +29,6 @@ describe('CompanyService', () => { it('should return an array of all companies', async () => { const mockData = prepareMockData(); - findAllProjectsStub.resolves(mockData.existingProjects); findAllCompaniesStub.resolves(mockData.existingCompanies); const res = await CompanyService.findAll(); @@ -44,7 +39,6 @@ describe('CompanyService', () => { it('should match the name of the companies', async () => { const mockData = prepareMockData(); - findAllProjectsStub.resolves(mockData.existingProjects); findAllCompaniesStub.resolves(mockData.existingCompanies); const res = await CompanyService.findAll(); @@ -150,34 +144,7 @@ function prepareMockData() { }, ]; - const existingProjects = [ - { - id: randomUUID(), - name: 'Zeitgeist P1', - description: 'Desc', - status: 'Not started', - startDate: new Date(), - totalHours: 10, - isChargeable: true, - area: SupportedDepartments.LEGAL, - createdAt: new Date(), - idCompany: idCompany1, - }, - { - id: randomUUID(), - name: 'Zeitgeist P2', - description: 'Desc', - status: 'Not started', - startDate: new Date(), - totalHours: 5, - isChargeable: true, - area: SupportedDepartments.ACCOUNTING, - createdAt: new Date(), - idCompany: idCompany1, - }, - ]; - - return { existingCompanies, existingProjects }; + return { existingCompanies }; } function prepareSingleFakeCompany() { diff --git a/src/core/app/services/__tests__/home.service.test.ts b/src/core/app/services/__tests__/home.service.test.ts index 987ab82f..2ec8e49c 100644 --- a/src/core/app/services/__tests__/home.service.test.ts +++ b/src/core/app/services/__tests__/home.service.test.ts @@ -3,9 +3,12 @@ import chai, { expect } from 'chai'; import chaiAsPromised from 'chai-as-promised'; import { randomUUID } from 'crypto'; import sinon from 'sinon'; +import { SupportedRoles } from '../../../../utils/enums'; import { CompanyRepository } from '../../../infra/repositories/company.repository'; import { EmployeeTaskRepository } from '../../../infra/repositories/employee-task.repository'; +import { EmployeeRepository } from '../../../infra/repositories/employee.repository'; import { ProjectRepository } from '../../../infra/repositories/project.repository'; +import { RoleRepository } from '../../../infra/repositories/role.repository'; import { TaskRepository } from '../../../infra/repositories/tasks.repository'; import { HomeService } from '../home.service'; @@ -16,12 +19,18 @@ describe('HomeService', () => { let findTaskByEmployeeIdStub: sinon.SinonStub; let findAllTasksStub: sinon.SinonStub; let findAllCompaniesStub: sinon.SinonStub; + let findEmployeeByEmail: sinon.SinonStub; + let findEmployeeById: sinon.SinonStub; + let findRoleById: sinon.SinonStub; beforeEach(() => { - findAllProjectsStub = sinon.stub(ProjectRepository, 'findAll'); + findAllProjectsStub = sinon.stub(ProjectRepository, 'findAllByRole'); findTaskByEmployeeIdStub = sinon.stub(EmployeeTaskRepository, 'findByEmployeeId'); findAllTasksStub = sinon.stub(TaskRepository, 'findAll'); findAllCompaniesStub = sinon.stub(CompanyRepository, 'findAll'); + findEmployeeByEmail = sinon.stub(EmployeeRepository, 'findByEmail'); + findEmployeeById = sinon.stub(EmployeeRepository, 'findById'); + findRoleById = sinon.stub(RoleRepository, 'findById'); }); afterEach(() => { @@ -29,9 +38,26 @@ describe('HomeService', () => { }); describe('getHomeInfo', () => { - it('should return the projects an employee has assigned and teh companies of those projects', async () => { + it('should return the projects an employee has assigned and the companies of those projects', async () => { const employeeId = randomUUID(); + const accountingRole = randomUUID(); + + const role = { + title: SupportedRoles.ACCOUNTING, + createdAr: new Date(), + }; + + const employee = { + id: employeeId, + firstName: 'John', + lastName: 'Doe', + email: 'joe.doe@email.com', + imageUrl: 'http://example.com/john.jpg', + createdAt: new Date(), + idRole: accountingRole, + }; + const companyId = randomUUID(); const companyId2 = randomUUID(); const existingCompanies = [ @@ -122,6 +148,10 @@ describe('HomeService', () => { }, ]; + findEmployeeByEmail.resolves(employee); + findEmployeeById.resolves(employee); + findRoleById.resolves(role); + findAllProjectsStub.resolves(existingProjects); findAllCompaniesStub.resolves(existingCompanies); findAllTasksStub.resolves(existingTasks); diff --git a/src/core/app/services/__tests__/project.service.test.ts b/src/core/app/services/__tests__/project.service.test.ts index ebc08495..139225bd 100644 --- a/src/core/app/services/__tests__/project.service.test.ts +++ b/src/core/app/services/__tests__/project.service.test.ts @@ -2,28 +2,40 @@ import { faker } from '@faker-js/faker'; import { expect } from 'chai'; import { randomUUID } from 'crypto'; import { default as Sinon, default as sinon } from 'sinon'; -import { ProjectCategory, ProjectPeriodicity, ProjectStatus, SupportedDepartments } from '../../../../utils/enums'; +import { + ProjectCategory, + ProjectPeriodicity, + ProjectStatus, + SupportedDepartments, + SupportedRoles, +} from '../../../../utils/enums'; import { ProjectEntity } from '../../../domain/entities/project.entity'; import { CompanyRepository } from '../../../infra/repositories/company.repository'; +import { EmployeeRepository } from '../../../infra/repositories/employee.repository'; import { ProjectRepository } from '../../../infra/repositories/project.repository'; +import { RoleRepository } from '../../../infra/repositories/role.repository'; import { CompanyService } from '../company.service'; import { ProjectService } from '../project.service'; describe('ProjectService', () => { let findProjectByIdStub: Sinon.SinonStub; let createProject: sinon.SinonStub; - let findAllStub: sinon.SinonStub; + let findAllByRoleStub: sinon.SinonStub; let findCompanyByIdStub: Sinon.SinonStub; let findProjectsByClientId: Sinon.SinonStub; let updateProjectStub: sinon.SinonStub; let updateProjectStatusStub: sinon.SinonStub; + let findEmployeeByEmail: sinon.SinonStub; + let findRoleById: sinon.SinonStub; beforeEach(() => { createProject = sinon.stub(ProjectRepository, 'createProject'); - findAllStub = sinon.stub(ProjectRepository, 'findAll'); + findAllByRoleStub = sinon.stub(ProjectRepository, 'findAllByRole'); findProjectByIdStub = sinon.stub(ProjectRepository, 'findById'); findProjectsByClientId = sinon.stub(ProjectRepository, 'findProjetsByClientId'); findCompanyByIdStub = sinon.stub(CompanyRepository, 'findById'); + findEmployeeByEmail = sinon.stub(EmployeeRepository, 'findByEmail'); + findRoleById = sinon.stub(RoleRepository, 'findById'); }); afterEach(() => { @@ -74,6 +86,20 @@ describe('ProjectService', () => { it('should create a project', async () => { const uuid = randomUUID(); const clientUuid = randomUUID(); + + const company = { + id: clientUuid, + name: 'Zeitgeist', + email: 'info@zeitgeist.mx', + phoneNumber: '1234567890', + landlinePhone: '0987654321', + archived: false, + createdAt: new Date(), + updatedAt: null, + idCompanyDirectContact: null, + idForm: null, + }; + const projectData = { id: uuid, name: 'Nuevo Proyecto de Desarrollo', @@ -91,6 +117,8 @@ describe('ProjectService', () => { createdAt: new Date('2024-04-19T01:23:49.555Z'), idCompany: clientUuid, }; + + findCompanyByIdStub.resolves(company); createProject.resolves(projectData); const newProject = await ProjectService.createProject(projectData); expect(newProject).to.equal(projectData); @@ -99,6 +127,23 @@ describe('ProjectService', () => { describe('getAllProjects', () => { it('should return all projects', async () => { + const accountingRole = randomUUID(); + + const role = { + title: SupportedRoles.ACCOUNTING, + createdAr: new Date(), + }; + + const employee = { + id: randomUUID(), + firstName: 'John', + lastName: 'Doe', + email: 'joe.doe@email.com', + imageUrl: 'http://example.com/john.jpg', + createdAt: new Date(), + idRole: accountingRole, + }; + const projects = [ { id: randomUUID(), @@ -113,7 +158,7 @@ describe('ProjectService', () => { periodicity: '1 week', isChargeable: true, isArchived: false, - area: 'Client', + area: SupportedDepartments.ACCOUNTING, createdAt: new Date('2024-04-19T01:23:49.555Z'), idCompany: randomUUID(), }, @@ -130,15 +175,17 @@ describe('ProjectService', () => { periodicity: '1 week', isChargeable: true, isArchived: false, - area: 'Client', + area: SupportedDepartments.ACCOUNTING, createdAt: new Date('2024-04-19T01:23:49.555Z'), idCompany: randomUUID(), }, ]; - findAllStub.resolves(projects); + findEmployeeByEmail.resolves(employee); + findRoleById.resolves(role); + findAllByRoleStub.resolves(projects); - const getProjects = await ProjectService.getAllProjects(); + const getProjects = await ProjectService.getDepartmentProjects(employee.email); expect(getProjects).eql(projects); }); diff --git a/src/core/app/services/home.service.ts b/src/core/app/services/home.service.ts index ceec4923..153419c8 100644 --- a/src/core/app/services/home.service.ts +++ b/src/core/app/services/home.service.ts @@ -1,6 +1,8 @@ import { CompanyRepository } from '../../infra/repositories/company.repository'; import { EmployeeTaskRepository } from '../../infra/repositories/employee-task.repository'; +import { EmployeeRepository } from '../../infra/repositories/employee.repository'; import { ProjectRepository } from '../../infra/repositories/project.repository'; +import { RoleRepository } from '../../infra/repositories/role.repository'; import { TaskRepository } from '../../infra/repositories/tasks.repository'; import { Home } from '../interfaces/home.interface'; @@ -14,7 +16,10 @@ import { Home } from '../interfaces/home.interface'; */ async function getMyInfo(idEmployee: string): Promise { try { - const projects = await ProjectRepository.findAll(); + const employee = await EmployeeRepository.findById(idEmployee); + const role = await RoleRepository.findById(employee.idRole); + + const projects = await ProjectRepository.findAllByRole(role.title); const employeeTask = await EmployeeTaskRepository.findByEmployeeId(idEmployee); const tasks = await TaskRepository.findAll(); const companies = await CompanyRepository.findAll(); @@ -48,6 +53,7 @@ async function getMyInfo(idEmployee: string): Promise { return homeInfo; } catch (error: unknown) { + console.log(error); throw new Error('An unexpected error occurred'); } } diff --git a/src/core/app/services/project.service.ts b/src/core/app/services/project.service.ts index dba8851d..431819cc 100644 --- a/src/core/app/services/project.service.ts +++ b/src/core/app/services/project.service.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto'; import { ProjectStatus } from '../../../utils/enums'; import { ProjectEntity } from '../../domain/entities/project.entity'; import { NotFoundError } from '../../errors/not-found.error'; +import { CompanyRepository } from '../../infra/repositories/company.repository'; import { ProjectRepository } from '../../infra/repositories/project.repository'; import { UpdateProjectBody } from '../interfaces/project.interface'; import { EmployeeService } from './employee.service'; @@ -24,33 +25,31 @@ interface CreateProjectData { * @param data The data required to create a project in the database * @returns The entity created */ -async function createProject(data: CreateProjectData): Promise { - const newProject = await ProjectRepository.createProject({ - id: randomUUID(), - name: data.name, - matter: data.matter ? data.matter : undefined, - description: data.description ? data.description : undefined, - area: data.area, - status: data.status, - category: data.category, - endDate: data.endDate, - idCompany: data.idCompany, - isChargeable: data.isChargeable ? data.isChargeable : undefined, - periodicity: data.periodicity, - startDate: data.startDate, - createdAt: new Date(), - }); +async function createProject(data: CreateProjectData): Promise { + try { + const company = await CompanyRepository.findById(data.idCompany); + if (company.archived) throw new Error('Cannot create projects for archived companies'); - return newProject; -} + const newProject = await ProjectRepository.createProject({ + id: randomUUID(), + name: data.name, + matter: data.matter ? data.matter : undefined, + description: data.description ? data.description : undefined, + area: data.area, + status: data.status, + category: data.category, + endDate: data.endDate, + idCompany: data.idCompany, + isChargeable: data.isChargeable ? data.isChargeable : undefined, + periodicity: data.periodicity, + startDate: data.startDate, + createdAt: new Date(), + }); -/** - * Gets all projects from the database - * - * @returns {Promise} - An array of project entities - */ -async function getAllProjects(): Promise { - return await ProjectRepository.findAll(); + return newProject; + } catch (error: any) { + throw new Error(error.message); + } } /** @@ -59,9 +58,12 @@ async function getAllProjects(): Promise { * @returns the projects only from a specific role */ async function getDepartmentProjects(email: string): Promise { - const role = await EmployeeService.findRoleByEmail(email); - - return await ProjectRepository.findAllByRole(role); + try { + const role = await EmployeeService.findRoleByEmail(email); + return await ProjectRepository.findAllByRole(role); + } catch (error) { + throw new Error('An unexpected error occured'); + } } /** @@ -149,7 +151,6 @@ async function updateProjectStatus(projectId: string, newStatus: ProjectStatus): export const ProjectService = { createProject, - getAllProjects, findProjectsClient, getProjectById, updateProject, diff --git a/src/core/infra/repositories/project.repository.ts b/src/core/infra/repositories/project.repository.ts index a1d09288..a4bae2d8 100644 --- a/src/core/infra/repositories/project.repository.ts +++ b/src/core/infra/repositories/project.repository.ts @@ -7,36 +7,6 @@ import { mapProjectEntityFromDbModel } from '../mappers/project-entity-from-db-m const RESOURCE_NAME = 'Project info'; -/** - * Finds all company entities in the database - * @version 2.0.0 - * @returns {Promise} a promise taht resolves to an array of company entities ordered by status - */ - -async function findAll(): Promise { - try { - const data: Array = await Prisma.$queryRaw` - SELECT * FROM project - ORDER BY case when status = 'Not started' then 1 - when status = 'In progress' then 2 - when status = 'In quotation' then 3 - when status = 'Under revision' then 4 - when status = 'Delayed' then 5 - when status = 'Postponed' then 6 - when status = 'Cancelled' then 7 - when status = 'Accepted' then 8 - when status = 'Done' then 9 - else 10 - end asc - `; - if (!data) throw new NotFoundError(`${RESOURCE_NAME} error`); - - return data.map(mapProjectEntityFromDbModel); - } catch (error: unknown) { - throw new Error(`${RESOURCE_NAME} repository error`); - } -} - /** * Retrieves all projects from a certain role, done projects appear last * @param role The role from the requester @@ -48,13 +18,31 @@ async function findAllByRole(role: SupportedRoles): Promise { let projects: PrismaProjectsRes; let doneProjects: PrismaProjectsRes; let res: Awaited; + if (role === SupportedRoles.ADMIN) { - projects = Prisma.project.findMany({ where: { NOT: { status: ProjectStatus.DONE } } }); + projects = Prisma.project.findMany({ + where: { + NOT: { status: ProjectStatus.DONE }, + }, + orderBy: { status: 'desc' }, + }); doneProjects = Prisma.project.findMany({ where: { status: ProjectStatus.DONE } }); res = (await Promise.all([projects, doneProjects])).flat(); } else { - projects = Prisma.project.findMany({ where: { area: role, NOT: { status: ProjectStatus.DONE } } }); - doneProjects = Prisma.project.findMany({ where: { status: ProjectStatus.DONE, area: role } }); + projects = Prisma.project.findMany({ + where: { + area: role, + NOT: { status: ProjectStatus.DONE }, + }, + orderBy: { status: 'desc' }, + }); + doneProjects = Prisma.project.findMany({ + where: { + status: ProjectStatus.DONE, + area: role, + }, + orderBy: { status: 'desc' }, + }); res = (await Promise.all([projects, doneProjects])).flat(); } if (!res) throw new NotFoundError(`${RESOURCE_NAME} error`); @@ -218,7 +206,6 @@ async function updateProjectStatus(projectId: string, newStatus: ProjectStatus): } export const ProjectRepository = { - findAll, findProjectStatusById, findById, findProjetsByClientId,