249 lines
6.5 KiB
TypeScript
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);
|
|
}
|
|
};
|
|
}
|