Files
bigcapital/packages/server/src/api/controllers/Projects/Projects.ts
2024-05-22 19:30:41 +02:00

249 lines
6.5 KiB
TypeScript

import { Router, Request, Response, NextFunction } from 'express';
import { check, param, query } from 'express-validator';
import { Service, Inject } from 'typedi';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import BaseController from '@/api/controllers/BaseController';
import { AbilitySubject, IProjectStatus, ProjectAction } from '@/interfaces';
import { ServiceError } from '@/exceptions';
import CheckPolicies from '@/api/middleware/CheckPolicies';
import { ProjectsApplication } from '@/services/Projects/Projects/ProjectsApplication';
@Service()
export class ProjectsController extends BaseController {
@Inject()
private projectsApplication: ProjectsApplication;
/**
* Router constructor method.
*/
router() {
const router = Router();
router.post(
'/',
CheckPolicies(ProjectAction.CREATE, AbilitySubject.Project),
[
check('contact_id').exists(),
check('name').exists().trim(),
check('deadline').exists().isISO8601(),
check('cost_estimate').exists().isDecimal(),
],
this.validationResult,
asyncMiddleware(this.createProject.bind(this))
);
router.post(
'/:id',
CheckPolicies(ProjectAction.EDIT, AbilitySubject.Project),
[
param('id').exists().isInt().toInt(),
check('contact_id').exists(),
check('name').exists().trim(),
check('deadline').exists().isISO8601(),
check('cost_estimate').exists().isDecimal(),
],
this.validationResult,
asyncMiddleware(this.editProject.bind(this))
);
router.patch(
'/:projectId/status',
CheckPolicies(ProjectAction.EDIT, AbilitySubject.Project),
[
param('projectId').exists().isInt().toInt(),
check('status')
.exists()
.isIn([IProjectStatus.InProgress, IProjectStatus.Closed]),
],
this.validationResult,
asyncMiddleware(this.editProject.bind(this))
);
router.get(
'/:id',
CheckPolicies(ProjectAction.VIEW, AbilitySubject.Project),
[param('id').exists().isInt().toInt()],
this.validationResult,
asyncMiddleware(this.getProject.bind(this))
);
router.get(
'/:projectId/billable/entries',
CheckPolicies(ProjectAction.VIEW, AbilitySubject.Project),
[
param('projectId').exists().isInt().toInt(),
query('billable_type').optional().toArray(),
query('to_date').optional().isISO8601(),
],
this.validationResult,
asyncMiddleware(this.projectBillableEntries.bind(this))
);
router.get(
'/',
CheckPolicies(ProjectAction.VIEW, AbilitySubject.Project),
[],
this.validationResult,
asyncMiddleware(this.getProjects.bind(this))
);
router.delete(
'/:id',
CheckPolicies(ProjectAction.DELETE, AbilitySubject.Project),
[param('id').exists().isInt().toInt()],
this.validationResult,
asyncMiddleware(this.deleteProject.bind(this))
);
return router;
}
/**
* Creates a new project.
* @param {Request} req -
* @param {Response} res -
* @param {NextFunction} next -
*/
private async createProject(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const projectDTO = this.matchedBodyData(req);
try {
const account = await this.projectsApplication.createProject(
tenantId,
projectDTO
);
return res.status(200).send({
id: account.id,
message: 'The project has been created successfully.',
});
} catch (error) {
next(error);
}
}
/**
* Edit project details.
* @param {Request} req
* @param {Response} res
* @return {Response}
*/
private async editProject(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { projectId } = req.params;
const editProjectDTO = this.matchedBodyData(req);
try {
const account = await this.projectsApplication.editProjectStatus(
tenantId,
projectId,
editProjectDTO.status
);
return res.status(200).send({
id: account.id,
message: 'The project has been edited successfully.',
});
} catch (error) {
next(error);
}
}
/**
* Get details of the given account.
* @param {Request} req
* @param {Response} res
* @return {Response}
*/
private async getProject(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { id: projectId } = req.params;
try {
const project = await this.projectsApplication.getProject(
tenantId,
projectId
);
return res.status(200).send({ project });
} catch (error) {
next(error);
}
}
/**
* Delete the given account.
* @param {Request} req
* @param {Response} res
* @return {Response}
*/
private async deleteProject(req: Request, res: Response, next: NextFunction) {
const { id: accountId } = req.params;
const { tenantId } = req;
try {
await this.projectsApplication.deleteProject(tenantId, accountId);
return res.status(200).send({
id: accountId,
message: 'The deleted project has been deleted successfully.',
});
} catch (error) {
next(error);
}
}
/**
* Retrieve accounts datatable list.
* @param {Request} req
* @param {Response} res
* @param {Response}
*/
private async getProjects(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
// Filter query.
const filter = {
sortOrder: 'desc',
columnSortBy: 'created_at',
inactiveMode: false,
...this.matchedQueryData(req),
};
try {
const projects = await this.projectsApplication.getProjects(
tenantId,
filter
);
return res.status(200).send({
projects,
});
} catch (error) {
next(error);
}
}
/**
* Retrieves the given billable entries of the given project.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Response}
*/
private projectBillableEntries = async (
req: Request,
res: Response,
next: NextFunction
) => {
const { tenantId } = req;
const { projectId } = req.params;
const query = this.matchedQueryData(req);
try {
const billableEntries =
await this.projectsApplication.getProjectBillableEntries(
tenantId,
projectId,
query
);
return res.status(200).send({
billableEntries,
});
} catch (error) {
next(error);
}
};
}