mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 22:30:31 +00:00
feat: approve and reject sale estimate. feat: initial receipts, invoices, estimates and bills views.
174 lines
5.3 KiB
TypeScript
174 lines
5.3 KiB
TypeScript
import { Service, Inject } from "typedi";
|
|
import validator from 'is-my-json-valid';
|
|
import { Request, Response, NextFunction } from 'express';
|
|
import { ServiceError } from 'exceptions';
|
|
import {
|
|
DynamicFilter,
|
|
DynamicFilterSortBy,
|
|
DynamicFilterViews,
|
|
DynamicFilterFilterRoles,
|
|
} from 'lib/DynamicFilter';
|
|
import {
|
|
validateFieldKeyExistance,
|
|
validateFilterRolesFieldsExistance,
|
|
} from 'lib/ViewRolesBuilder';
|
|
import {
|
|
IDynamicListFilterDTO,
|
|
IFilterRole,
|
|
IDynamicListService,
|
|
IModel,
|
|
} from 'interfaces';
|
|
import TenancyService from 'services/Tenancy/TenancyService';
|
|
|
|
const ERRORS = {
|
|
VIEW_NOT_FOUND: 'view_not_found',
|
|
SORT_COLUMN_NOT_FOUND: 'sort_column_not_found',
|
|
FILTER_ROLES_FIELDS_NOT_FOUND: 'filter_roles_fields_not_found',
|
|
};
|
|
|
|
@Service()
|
|
export default class DynamicListService implements IDynamicListService {
|
|
@Inject()
|
|
tenancy: TenancyService;
|
|
|
|
/**
|
|
* Retreive custom view or throws error not found.
|
|
* @param {number} tenantId
|
|
* @param {number} viewId
|
|
* @return {Promise<IView>}
|
|
*/
|
|
private async getCustomViewOrThrowError(tenantId: number, viewId: number, model: IModel) {
|
|
const { viewRepository } = this.tenancy.repositories(tenantId);
|
|
const view = await viewRepository.findOneById(viewId, 'roles');
|
|
|
|
if (!view || view.resourceModel !== model.name) {
|
|
throw new ServiceError(ERRORS.VIEW_NOT_FOUND);
|
|
}
|
|
return view;
|
|
}
|
|
|
|
/**
|
|
* Validates the sort column whether exists.
|
|
* @param {IModel} model
|
|
* @param {string} columnSortBy - Sort column
|
|
* @throws {ServiceError}
|
|
*/
|
|
private validateSortColumnExistance(model: any, columnSortBy: string) {
|
|
const notExistsField = validateFieldKeyExistance(model, columnSortBy);
|
|
|
|
if (!notExistsField) {
|
|
throw new ServiceError(ERRORS.SORT_COLUMN_NOT_FOUND);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validates existance the fields of filter roles.
|
|
* @param {IModel} model
|
|
* @param {IFilterRole[]} filterRoles
|
|
* @throws {ServiceError}
|
|
*/
|
|
private validateRolesFieldsExistance(model: IModel, filterRoles: IFilterRole[]) {
|
|
const invalidFieldsKeys = validateFilterRolesFieldsExistance(model, filterRoles);
|
|
|
|
if (invalidFieldsKeys.length > 0) {
|
|
throw new ServiceError(ERRORS.FILTER_ROLES_FIELDS_NOT_FOUND);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validates filter roles schema.
|
|
* @param {IFilterRole[]} filterRoles
|
|
*/
|
|
private validateFilterRolesSchema(filterRoles: IFilterRole[]) {
|
|
const validate = validator({
|
|
required: true,
|
|
type: 'object',
|
|
properties: {
|
|
condition: { type: 'string' },
|
|
fieldKey: { required: true, type: 'string' },
|
|
value: { required: true },
|
|
},
|
|
});
|
|
const invalidFields = filterRoles.filter((filterRole) => {
|
|
const isValid = validate(filterRole);
|
|
return isValid ? false : true;
|
|
});
|
|
if (invalidFields.length > 0) {
|
|
throw new ServiceError('stringified_filter_roles_invalid');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dynamic listing.
|
|
* @param {number} tenantId
|
|
* @param {IModel} model
|
|
* @param {IDynamicListFilterDTO} filter
|
|
*/
|
|
public async dynamicList(tenantId: number, model: IModel, filter: IDynamicListFilterDTO) {
|
|
const dynamicFilter = new DynamicFilter(model);
|
|
|
|
// Custom view filter roles.
|
|
if (filter.customViewId) {
|
|
const view = await this.getCustomViewOrThrowError(tenantId, filter.customViewId, model);
|
|
|
|
const viewFilter = new DynamicFilterViews(view);
|
|
dynamicFilter.setFilter(viewFilter);
|
|
}
|
|
// Sort by the given column.
|
|
if (filter.columnSortBy) {
|
|
this.validateSortColumnExistance(model, filter.columnSortBy);
|
|
|
|
const sortByFilter = new DynamicFilterSortBy(
|
|
filter.columnSortBy, filter.sortOrder
|
|
);
|
|
dynamicFilter.setFilter(sortByFilter);
|
|
}
|
|
// Filter roles.
|
|
if (filter.filterRoles.length > 0) {
|
|
const filterRoles = filter.filterRoles.map((filterRole, index) => ({
|
|
...filterRole,
|
|
index: index + 1,
|
|
}));
|
|
this.validateFilterRolesSchema(filterRoles);
|
|
this.validateRolesFieldsExistance(model, filterRoles);
|
|
|
|
// Validate the model resource fields.
|
|
const dynamicFilterRoles = new DynamicFilterFilterRoles(filterRoles);
|
|
dynamicFilter.setFilter(dynamicFilterRoles);
|
|
}
|
|
return dynamicFilter;
|
|
}
|
|
|
|
/**
|
|
* Middleware to catch services errors
|
|
* @param {Error} error
|
|
* @param {Request} req
|
|
* @param {Response} res
|
|
* @param {NextFunction} next
|
|
*/
|
|
public handlerErrorsToResponse(error: Error, req: Request, res: Response, next: NextFunction) {
|
|
if (error instanceof ServiceError) {
|
|
if (error.errorType === 'sort_column_not_found') {
|
|
return res.boom.badRequest(null, {
|
|
errors: [{ type: 'SORT.COLUMN.NOT.FOUND', code: 200 }],
|
|
});
|
|
}
|
|
if (error.errorType === 'view_not_found') {
|
|
return res.boom.badRequest(null, {
|
|
errors: [{ type: 'CUSTOM.VIEW.NOT.FOUND', code: 100 }],
|
|
});
|
|
}
|
|
if (error.errorType === 'filter_roles_fields_not_found') {
|
|
return res.boom.badRequest(null, {
|
|
errors: [{ type: 'FILTER.ROLES.FIELDS.NOT.FOUND', code: 300 }],
|
|
});
|
|
}
|
|
if (error.errorType === 'stringified_filter_roles_invalid') {
|
|
return res.boom.badRequest(null, {
|
|
errors: [{ type: 'STRINGIFIED_FILTER_ROLES_INVALID', code: 400 }],
|
|
});
|
|
}
|
|
}
|
|
next(error);
|
|
}
|
|
} |