mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 13:50:31 +00:00
feat: optimize dynamic list service.
feat: inactive mode for accounts, items, customers and vendors services.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { difference, chain, uniq } from 'lodash';
|
||||
import { kebabCase } from 'lodash';
|
||||
import R from 'ramda';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import {
|
||||
@@ -606,6 +607,17 @@ export default class AccountsService {
|
||||
this.eventDispatcher.dispatch(events.accounts.onActivated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsees accounts list filter DTO.
|
||||
* @param filterDTO
|
||||
* @returns
|
||||
*/
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(
|
||||
this.dynamicListService.parseStringifiedFilter
|
||||
)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve accounts datatable list.
|
||||
* @param {number} tenantId
|
||||
@@ -613,21 +625,26 @@ export default class AccountsService {
|
||||
*/
|
||||
public async getAccountsList(
|
||||
tenantId: number,
|
||||
filter: IAccountsFilter
|
||||
filterDTO: IAccountsFilter
|
||||
): Promise<{ accounts: IAccountResponse[]; filterMeta: IFilterMeta }> {
|
||||
const { Account } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses the stringified filter roles.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
Account,
|
||||
filter
|
||||
);
|
||||
|
||||
this.logger.info('[accounts] trying to get accounts datatable list.', {
|
||||
tenantId,
|
||||
filter,
|
||||
});
|
||||
const accounts = await Account.query().onBuild((builder) => {
|
||||
dynamicList.buildQuery()(builder);
|
||||
builder.modify('inactiveMode', filter.inactiveMode);
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -727,10 +744,11 @@ export default class AccountsService {
|
||||
}));
|
||||
return flatToNestedArray(
|
||||
this.i18nService.i18nMapper(_accounts, ['account_type_label'], tenantId),
|
||||
{
|
||||
id: 'id',
|
||||
parentId: 'parent_account_id',
|
||||
});
|
||||
{
|
||||
id: 'id',
|
||||
parentId: 'parent_account_id',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { omit, defaultTo } from 'lodash';
|
||||
import async from 'async';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
@@ -265,6 +265,16 @@ export default class CustomersService {
|
||||
return this.transformContactToCustomer(contact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses customers list filter DTO.
|
||||
* @param filterDTO -
|
||||
*/
|
||||
private parseCustomersListFilterDTO(filterDTO) {
|
||||
return R.compose(
|
||||
this.dynamicListService.parseStringifiedFilter
|
||||
)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve customers paginated list.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
@@ -272,7 +282,7 @@ export default class CustomersService {
|
||||
*/
|
||||
public async getCustomersList(
|
||||
tenantId: number,
|
||||
customersFilter: ICustomersFilter
|
||||
filterDTO: ICustomersFilter
|
||||
): Promise<{
|
||||
customers: ICustomer[];
|
||||
pagination: IPaginationMeta;
|
||||
@@ -280,17 +290,23 @@ export default class CustomersService {
|
||||
}> {
|
||||
const { Customer } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses customers list filter DTO.
|
||||
const filter = this.parseCustomersListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list.
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
Customer,
|
||||
customersFilter
|
||||
filter
|
||||
);
|
||||
|
||||
// Customers.
|
||||
const { results, pagination } = await Customer.query()
|
||||
.onBuild((query) => {
|
||||
dynamicList.buildQuery()(query);
|
||||
.onBuild((builder) => {
|
||||
dynamicList.buildQuery()(builder);
|
||||
builder.modify('inactiveMode', filter.inactiveMode);
|
||||
})
|
||||
.pagination(customersFilter.page - 1, customersFilter.pageSize);
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
return {
|
||||
customers: results.map(this.transformContactToCustomer),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { intersection, defaultTo } from 'lodash';
|
||||
import { defaultTo } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
@@ -255,6 +256,12 @@ export default class VendorsService {
|
||||
);
|
||||
}
|
||||
|
||||
private parseVendorsListFilterDTO(filterDTO) {
|
||||
return R.compose(
|
||||
this.dynamicListService.parseStringifiedFilter
|
||||
)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve vendors datatable list.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
@@ -262,7 +269,7 @@ export default class VendorsService {
|
||||
*/
|
||||
public async getVendorsList(
|
||||
tenantId: number,
|
||||
vendorsFilter: IVendorsFilter
|
||||
filterDTO: IVendorsFilter
|
||||
): Promise<{
|
||||
vendors: IVendor[];
|
||||
pagination: IPaginationMeta;
|
||||
@@ -270,21 +277,28 @@ export default class VendorsService {
|
||||
}> {
|
||||
const { Vendor } = this.tenancy.models(tenantId);
|
||||
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
// Parses vendors list filter DTO.
|
||||
const filter = this.parseVendorsListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
Vendor,
|
||||
vendorsFilter
|
||||
filter
|
||||
);
|
||||
|
||||
// Vendors list.
|
||||
const { results, pagination } = await Vendor.query()
|
||||
.onBuild((builder) => {
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
dynamicList.buildQuery()(builder);
|
||||
builder.modify('inactiveMode', filter.inactiveMode);
|
||||
})
|
||||
.pagination(vendorsFilter.page - 1, vendorsFilter.pageSize);
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
return {
|
||||
vendors: results,
|
||||
pagination,
|
||||
filterMeta: dynamicFilter.getResponseMeta(),
|
||||
filterMeta: dynamicList.getResponseMeta(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
|
||||
export default class DynamicListAbstruct {
|
||||
|
||||
}
|
||||
52
server/src/services/DynamicListing/DynamicListCustomView.ts
Normal file
52
server/src/services/DynamicListing/DynamicListCustomView.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import DynamicListAbstruct from './DynamicListAbstruct';
|
||||
import DynamicFilterViews from 'lib/DynamicFilter/DynamicFilterViews';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import HasTenancyService from 'services/Tenancy/TenancyService';
|
||||
import {ERRORS } from './constants';
|
||||
import { IModel }from 'interfaces';
|
||||
|
||||
@Service()
|
||||
export default class DynamicListCustomView extends DynamicListAbstruct {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Retreive custom view or throws error not found.
|
||||
* @param {number} tenantId
|
||||
* @param {number} viewId
|
||||
* @return {Promise<IView>}
|
||||
*/
|
||||
private getCustomViewOrThrowError = async (
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dynamic list custom view.
|
||||
* @param {IModel} model
|
||||
* @param {number} customViewId
|
||||
* @returns
|
||||
*/
|
||||
public dynamicListCustomView = async (
|
||||
tenantId: number,
|
||||
model,
|
||||
customViewId: number
|
||||
) => {
|
||||
const view = await this.getCustomViewOrThrowError(
|
||||
tenantId,
|
||||
customViewId,
|
||||
model
|
||||
);
|
||||
return new DynamicFilterViews(view);
|
||||
};
|
||||
}
|
||||
103
server/src/services/DynamicListing/DynamicListFilterRoles.ts
Normal file
103
server/src/services/DynamicListing/DynamicListFilterRoles.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { Service } from 'typedi';
|
||||
import * as R from 'ramda';
|
||||
import validator from 'is-my-json-valid';
|
||||
import { IFilterRole, IModel } from 'interfaces';
|
||||
import DynamicListAbstruct from './DynamicListAbstruct';
|
||||
import DynamicFilterFilterRoles from 'lib/DynamicFilter/DynamicFilterFilterRoles';
|
||||
import { ERRORS } from './constants';
|
||||
import { ServiceError } from 'exceptions';
|
||||
|
||||
@Service()
|
||||
export default class DynamicListFilterRoles extends DynamicListAbstruct {
|
||||
/**
|
||||
* Validates filter roles schema.
|
||||
* @param {IFilterRole[]} filterRoles - Filter roles.
|
||||
*/
|
||||
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) => {
|
||||
return !validate(filterRole);
|
||||
});
|
||||
if (invalidFields.length > 0) {
|
||||
throw new ServiceError(ERRORS.STRINGIFIED_FILTER_ROLES_INVALID);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve filter roles fields key that not exists on the given model.
|
||||
* @param {IModel} model
|
||||
* @param {IFilterRole} filterRoles
|
||||
* @returns {string[]}
|
||||
*/
|
||||
private getFilterRolesFieldsNotExist = (
|
||||
model,
|
||||
filterRoles: IFilterRole[]
|
||||
): string[] => {
|
||||
return filterRoles
|
||||
.filter((filterRole) => !model.getField(filterRole.fieldKey))
|
||||
.map((filterRole) => filterRole.fieldKey);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates existance the fields of filter roles.
|
||||
* @param {IModel} model
|
||||
* @param {IFilterRole[]} filterRoles
|
||||
* @throws {ServiceError}
|
||||
*/
|
||||
private validateFilterRolesFieldsExistance = (
|
||||
model: IModel,
|
||||
filterRoles: IFilterRole[]
|
||||
) => {
|
||||
const invalidFieldsKeys = this.getFilterRolesFieldsNotExist(
|
||||
model,
|
||||
filterRoles
|
||||
);
|
||||
if (invalidFieldsKeys.length > 0) {
|
||||
throw new ServiceError(ERRORS.FILTER_ROLES_FIELDS_NOT_FOUND);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Associate index to filter roles.
|
||||
* @param {IFilterRole[]} filterRoles
|
||||
* @returns {IFilterRole[]}
|
||||
*/
|
||||
private incrementFilterRolesIndex = (
|
||||
filterRoles: IFilterRole[]
|
||||
): IFilterRole[] => {
|
||||
return filterRoles.map((filterRole, index) => ({
|
||||
...filterRole,
|
||||
index: index + 1,
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Dynamic list filter roles.
|
||||
* @param {IModel} model
|
||||
* @param {IFilterRole[]} filterRoles
|
||||
* @returns {DynamicFilterFilterRoles}
|
||||
*/
|
||||
public dynamicList = (
|
||||
model: IModel,
|
||||
filterRoles: IFilterRole[]
|
||||
): DynamicFilterFilterRoles => {
|
||||
const filterRolesParsed = R.compose(this.incrementFilterRolesIndex)(
|
||||
filterRoles
|
||||
);
|
||||
// Validate filter roles json schema.
|
||||
this.validateFilterRolesSchema(filterRolesParsed);
|
||||
|
||||
// Validate the model resource fields.
|
||||
this.validateFilterRolesFieldsExistance(model, filterRoles);
|
||||
|
||||
return new DynamicFilterFilterRoles(filterRolesParsed);
|
||||
};
|
||||
}
|
||||
@@ -1,161 +1,88 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import validator from 'is-my-json-valid';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { castArray, isEmpty } from 'lodash';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import { DynamicFilter } from 'lib/DynamicFilter';
|
||||
import {
|
||||
DynamicFilter,
|
||||
DynamicFilterSortBy,
|
||||
DynamicFilterViews,
|
||||
DynamicFilterFilterRoles,
|
||||
} from 'lib/DynamicFilter';
|
||||
import {
|
||||
validateFieldKeyExistance,
|
||||
validateFilterRolesFieldsExistance,
|
||||
} from 'lib/ViewRolesBuilder';
|
||||
import {
|
||||
IDynamicListFilterDTO,
|
||||
IFilterRole,
|
||||
IDynamicListFilter,
|
||||
IDynamicListService,
|
||||
IFilterRole,
|
||||
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',
|
||||
};
|
||||
import DynamicListFilterRoles from './DynamicListFilterRoles';
|
||||
import DynamicListSortBy from './DynamicListSortBy';
|
||||
import DynamicListCustomView from './DynamicListCustomView';
|
||||
|
||||
@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');
|
||||
@Inject()
|
||||
dynamicListFilterRoles: DynamicListFilterRoles;
|
||||
|
||||
if (!view || view.resourceModel !== model.name) {
|
||||
throw new ServiceError(ERRORS.VIEW_NOT_FOUND);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
@Inject()
|
||||
dynamicListSortBy: DynamicListSortBy;
|
||||
|
||||
@Inject()
|
||||
dynamicListView: DynamicListCustomView;
|
||||
|
||||
/**
|
||||
* Validates the sort column whether exists.
|
||||
* @param {IModel} model
|
||||
* @param {string} columnSortBy - Sort column
|
||||
* @throws {ServiceError}
|
||||
* Parses filter DTO.
|
||||
*/
|
||||
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');
|
||||
}
|
||||
}
|
||||
private parseFilterObject = (model, filterDTO) => {
|
||||
return {
|
||||
// Merges the default properties with filter object.
|
||||
...model.defaultSort ? {
|
||||
sortOrder: model.defaultSort.sortOrder,
|
||||
columnSortBy: model.defaultSort.sortOrder,
|
||||
} : {},
|
||||
...filterDTO,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Dynamic listing.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {IModel} model - Model.
|
||||
* @param {IDynamicListFilterDTO} filter - Dynamic filter DTO.
|
||||
* @param {IDynamicListFilter} filter - Dynamic filter DTO.
|
||||
*/
|
||||
public async dynamicList(
|
||||
public dynamicList = async (
|
||||
tenantId: number,
|
||||
model: IModel,
|
||||
filter: IDynamicListFilterDTO
|
||||
) {
|
||||
filter: IDynamicListFilter
|
||||
) => {
|
||||
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);
|
||||
// Parses the filter object.
|
||||
const parsedFilter = this.parseFilterObject(model, filter);
|
||||
|
||||
const sortByFilter = new DynamicFilterSortBy(
|
||||
filter.columnSortBy,
|
||||
filter.sortOrder
|
||||
// Custom view filter roles.
|
||||
// if (filter.customViewId) {
|
||||
// const dynamicListCustomView = this.dynamicListView.dynamicListCustomView();
|
||||
|
||||
// dynamicFilter.setFilter(dynamicListCustomView);
|
||||
// }
|
||||
// Sort by the given column.
|
||||
if (parsedFilter.columnSortBy) {
|
||||
const dynmaicListSortBy = this.dynamicListSortBy.dynamicSortBy(
|
||||
model,
|
||||
parsedFilter.columnSortBy,
|
||||
parsedFilter.sortOrder,
|
||||
);
|
||||
dynamicFilter.setFilter(sortByFilter);
|
||||
dynamicFilter.setFilter(dynmaicListSortBy);
|
||||
}
|
||||
// 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);
|
||||
if (!isEmpty(parsedFilter.filterRoles)) {
|
||||
const dynamicFilterRoles = this.dynamicListFilterRoles.dynamicList(
|
||||
model,
|
||||
parsedFilter.filterRoles
|
||||
);
|
||||
dynamicFilter.setFilter(dynamicFilterRoles);
|
||||
}
|
||||
return dynamicFilter;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Middleware to catch services errors
|
||||
@@ -173,25 +100,62 @@ export default class DynamicListService implements IDynamicListService {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'sort_column_not_found') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'SORT.COLUMN.NOT.FOUND', code: 200 }],
|
||||
errors: [
|
||||
{
|
||||
type: 'SORT.COLUMN.NOT.FOUND',
|
||||
message: '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 }],
|
||||
errors: [
|
||||
{
|
||||
type: 'CUSTOM.VIEW.NOT.FOUND',
|
||||
message: '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 }],
|
||||
errors: [
|
||||
{
|
||||
type: 'FILTER.ROLES.FIELDS.NOT.FOUND',
|
||||
message: '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 }],
|
||||
errors: [
|
||||
{
|
||||
type: 'STRINGIFIED_FILTER_ROLES_INVALID',
|
||||
message: 'Stringified filter roles json invalid.',
|
||||
code: 400,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses stringified filter roles.
|
||||
* @param {string} stringifiedFilterRoles - Stringified filter roles.
|
||||
*/
|
||||
public parseStringifiedFilter = (filterRoles: IDynamicListFilter) => {
|
||||
return {
|
||||
...filterRoles,
|
||||
filterRoles: filterRoles.stringifiedFilterRoles
|
||||
? castArray(JSON.parse(filterRoles.stringifiedFilterRoles))
|
||||
: [],
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
40
server/src/services/DynamicListing/DynamicListSortBy.ts
Normal file
40
server/src/services/DynamicListing/DynamicListSortBy.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { Service } from 'typedi';
|
||||
import DynamicListAbstruct from './DynamicListAbstruct';
|
||||
import DynamicFilterSortBy from 'lib/DynamicFilter/DynamicFilterSortBy';
|
||||
import { IModel, ISortOrder } from 'interfaces';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import { ERRORS } from './constants';
|
||||
|
||||
@Service()
|
||||
export default class DynamicListSortBy extends DynamicListAbstruct {
|
||||
/**
|
||||
* Dynamic list sort by.
|
||||
* @param {IModel} model
|
||||
* @param {string} columnSortBy
|
||||
* @param {ISortOrder} sortOrder
|
||||
* @returns {DynamicFilterSortBy}
|
||||
*/
|
||||
public dynamicSortBy(
|
||||
model: IModel,
|
||||
columnSortBy: string,
|
||||
sortOrder: ISortOrder
|
||||
) {
|
||||
this.validateSortColumnExistance(model, columnSortBy);
|
||||
|
||||
return new DynamicFilterSortBy(columnSortBy, sortOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the sort column whether exists.
|
||||
* @param {IModel} model - Model.
|
||||
* @param {string} columnSortBy - Sort column
|
||||
* @throws {ServiceError}
|
||||
*/
|
||||
private validateSortColumnExistance(model: any, columnSortBy: string) {
|
||||
const field = model.getField(columnSortBy);
|
||||
|
||||
if (!field) {
|
||||
throw new ServiceError(ERRORS.SORT_COLUMN_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
6
server/src/services/DynamicListing/constants.ts
Normal file
6
server/src/services/DynamicListing/constants.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const ERRORS = {
|
||||
STRINGIFIED_FILTER_ROLES_INVALID: 'stringified_filter_roles_invalid',
|
||||
VIEW_NOT_FOUND: 'view_not_found',
|
||||
SORT_COLUMN_NOT_FOUND: 'sort_column_not_found',
|
||||
FILTER_ROLES_FIELDS_NOT_FOUND: 'filter_roles_fields_not_found',
|
||||
};
|
||||
0
server/src/services/DynamicListing/validators.ts
Normal file
0
server/src/services/DynamicListing/validators.ts
Normal file
@@ -1,6 +1,7 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { difference, sumBy, omit, map } from 'lodash';
|
||||
import { difference, sumBy, omit } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
@@ -630,6 +631,16 @@ export default class ExpensesService implements IExpensesService {
|
||||
return expenses.filter((expense) => expense.publishedAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses filter DTO of expenses list.
|
||||
* @param filterDTO -
|
||||
*/
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(
|
||||
this.dynamicListService.parseStringifiedFilter,
|
||||
)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve expenses datatable lsit.
|
||||
* @param {number} tenantId
|
||||
@@ -638,35 +649,41 @@ export default class ExpensesService implements IExpensesService {
|
||||
*/
|
||||
public async getExpensesList(
|
||||
tenantId: number,
|
||||
expensesFilter: IExpensesFilter
|
||||
filterDTO: IExpensesFilter
|
||||
): Promise<{
|
||||
expenses: IExpense[];
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
const { Expense } = this.tenancy.models(tenantId);
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
|
||||
// Parses list filter DTO.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
Expense,
|
||||
expensesFilter
|
||||
filter,
|
||||
);
|
||||
|
||||
this.logger.info('[expense] trying to get expenses datatable list.', {
|
||||
tenantId,
|
||||
expensesFilter,
|
||||
filter,
|
||||
});
|
||||
const { results, pagination } = await Expense.query()
|
||||
.onBuild((builder) => {
|
||||
builder.withGraphFetched('paymentAccount');
|
||||
builder.withGraphFetched('categories.expenseAccount');
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
|
||||
dynamicList.buildQuery()(builder);
|
||||
})
|
||||
.pagination(expensesFilter.page - 1, expensesFilter.pageSize);
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
return {
|
||||
expenses: results,
|
||||
pagination,
|
||||
filterMeta: dynamicFilter.getResponseMeta(),
|
||||
filterMeta: dynamicList.getResponseMeta(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ export default class JournalSheetService {
|
||||
fromRange: null,
|
||||
toRange: null,
|
||||
accountsIds: [],
|
||||
transactionTypes: [],
|
||||
numberFormat: {
|
||||
noCents: false,
|
||||
divideOn1000: false,
|
||||
@@ -107,6 +106,13 @@ export default class JournalSheetService {
|
||||
}
|
||||
query.modify('filterDateRange', filter.fromDate, filter.toDate);
|
||||
query.orderBy(['date', 'createdAt', 'indexGroup', 'index']);
|
||||
|
||||
if (filter.transactionType) {
|
||||
return query.where('reference_type', filter.transactionType);
|
||||
}
|
||||
if (filter.transactionType && filter.transactionId) {
|
||||
return query.where('reference_id', filter.transactionId);
|
||||
}
|
||||
});
|
||||
// Transform the transactions array to journal collection.
|
||||
const transactionsJournal = Journal.fromTransactions(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { omit } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
@@ -225,8 +226,8 @@ export default class InventoryAdjustmentService {
|
||||
|
||||
/**
|
||||
* Publish the inventory adjustment transaction.
|
||||
* @param tenantId
|
||||
* @param inventoryAdjustmentId
|
||||
* @param {number} tenantId
|
||||
* @param {number} inventoryAdjustmentId
|
||||
*/
|
||||
async publishInventoryAdjustment(
|
||||
tenantId: number,
|
||||
@@ -265,6 +266,17 @@ export default class InventoryAdjustmentService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses inventory adjustments list filter DTO.
|
||||
* @param filterDTO
|
||||
* @returns
|
||||
*/
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(
|
||||
this.dynamicListService.parseStringifiedFilter,
|
||||
)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the inventory adjustments paginated list.
|
||||
* @param {number} tenantId
|
||||
@@ -272,17 +284,21 @@ export default class InventoryAdjustmentService {
|
||||
*/
|
||||
async getInventoryAdjustments(
|
||||
tenantId: number,
|
||||
adjustmentsFilter: IInventoryAdjustmentsFilter
|
||||
filterDTO: IInventoryAdjustmentsFilter
|
||||
): Promise<{
|
||||
inventoryAdjustments: IInventoryAdjustment[];
|
||||
pagination: IPaginationMeta;
|
||||
}> {
|
||||
const { InventoryAdjustment } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses inventory adjustments list filter DTO.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
InventoryAdjustment,
|
||||
adjustmentsFilter
|
||||
filter,
|
||||
);
|
||||
const { results, pagination } = await InventoryAdjustment.query()
|
||||
.onBuild((query) => {
|
||||
@@ -291,7 +307,7 @@ export default class InventoryAdjustmentService {
|
||||
|
||||
dynamicFilter.buildQuery()(query);
|
||||
})
|
||||
.pagination(adjustmentsFilter.page - 1, adjustmentsFilter.pageSize);
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
return {
|
||||
inventoryAdjustments: results,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Inject } from 'typedi';
|
||||
import { difference } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
@@ -377,6 +378,18 @@ export default class ItemCategoriesService implements IItemCategoriesService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses items categories filter DTO.
|
||||
* @param {} filterDTO
|
||||
* @returns
|
||||
*/
|
||||
private parsesListFilterDTO(filterDTO) {
|
||||
return R.compose(
|
||||
// Parses stringified filter roles.
|
||||
this.dynamicListService.parseStringifiedFilter,
|
||||
)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve item categories list.
|
||||
* @param {number} tenantId
|
||||
@@ -384,10 +397,15 @@ export default class ItemCategoriesService implements IItemCategoriesService {
|
||||
*/
|
||||
public async getItemCategoriesList(
|
||||
tenantId: number,
|
||||
filter: IItemCategoriesFilter,
|
||||
filterDTO: IItemCategoriesFilter,
|
||||
authorizedUser: ISystemUser
|
||||
): Promise<{ itemCategories: IItemCategory[]; filterMeta: IFilterMeta }> {
|
||||
const { ItemCategory } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses list filter DTO.
|
||||
const filter = this.parsesListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
ItemCategory,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { defaultTo, difference } from 'lodash';
|
||||
import { defaultTo } from 'lodash';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
@@ -525,20 +526,37 @@ export default class ItemsService implements IItemsService {
|
||||
return this.transformItemToResponse(tenantId, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses items list filter DTO.
|
||||
* @param {} filterDTO - Filter DTO.
|
||||
*/
|
||||
private parseItemsListFilterDTO(filterDTO) {
|
||||
return R.compose(
|
||||
this.dynamicListService.parseStringifiedFilter,
|
||||
)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve items datatable list.
|
||||
* @param {number} tenantId
|
||||
* @param {IItemsFilter} itemsFilter
|
||||
*/
|
||||
public async itemsList(tenantId: number, itemsFilter: IItemsFilter) {
|
||||
public async itemsList(tenantId: number, filterDTO: IItemsFilter) {
|
||||
const { Item } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses items list filter DTO.
|
||||
const filter = this.parseItemsListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
Item,
|
||||
itemsFilter
|
||||
filter
|
||||
);
|
||||
const { results: items, pagination } = await Item.query()
|
||||
.onBuild((builder) => {
|
||||
builder.modify('inactiveMode', filter.inactiveMode);
|
||||
|
||||
builder.withGraphFetched('inventoryAccount');
|
||||
builder.withGraphFetched('sellAccount');
|
||||
builder.withGraphFetched('costAccount');
|
||||
@@ -546,7 +564,7 @@ export default class ItemsService implements IItemsService {
|
||||
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
})
|
||||
.pagination(itemsFilter.page - 1, itemsFilter.pageSize);
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
const results = items.map((item) =>
|
||||
this.transformItemToResponse(tenantId, item)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { difference, sumBy, omit, map } from 'lodash';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import moment from 'moment';
|
||||
import { ServiceError, ServiceErrors } from 'exceptions';
|
||||
import * as R from 'ramda';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import {
|
||||
IManualJournalDTO,
|
||||
IManualJournalsService,
|
||||
@@ -768,33 +769,47 @@ export default class ManualJournalsService implements IManualJournalsService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses filter DTO of the manual journals list.
|
||||
* @param filterDTO
|
||||
*/
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(
|
||||
this.dynamicListService.parseStringifiedFilter
|
||||
)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve manual journals datatable list.
|
||||
* @param {number} tenantId
|
||||
* @param {IManualJournalsFilter} filter
|
||||
* @param {number} tenantId -
|
||||
* @param {IManualJournalsFilter} filter -
|
||||
*/
|
||||
public async getManualJournals(
|
||||
tenantId: number,
|
||||
filter: IManualJournalsFilter
|
||||
filterDTO: IManualJournalsFilter
|
||||
): Promise<{
|
||||
manualJournals: IManualJournal;
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
const { ManualJournal } = this.tenancy.models(tenantId);
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
|
||||
// Parses filter DTO.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic service.
|
||||
const dynamicService = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
ManualJournal,
|
||||
filter
|
||||
);
|
||||
|
||||
this.logger.info('[manual_journals] trying to get manual journals list.', {
|
||||
tenantId,
|
||||
filter,
|
||||
});
|
||||
const { results, pagination } = await ManualJournal.query()
|
||||
.onBuild((builder) => {
|
||||
dynamicList.buildQuery()(builder);
|
||||
dynamicService.buildQuery()(builder);
|
||||
builder.withGraphFetched('entries.account');
|
||||
})
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
@@ -802,7 +817,7 @@ export default class ManualJournalsService implements IManualJournalsService {
|
||||
return {
|
||||
manualJournals: results,
|
||||
pagination,
|
||||
filterMeta: dynamicList.getResponseMeta(),
|
||||
filterMeta: dynamicService.getResponseMeta(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { omit, sumBy, difference } from 'lodash';
|
||||
import { sumBy, difference } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
@@ -25,7 +26,7 @@ import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
||||
import { entriesAmountDiff, formatDateFields } from 'utils';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import { ACCOUNT_PARENT_TYPE, ACCOUNT_TYPE } from 'data/AccountTypes';
|
||||
import { ACCOUNT_TYPE } from 'data/AccountTypes';
|
||||
import VendorsService from 'services/Contacts/VendorsService';
|
||||
import { ERRORS } from './constants';
|
||||
|
||||
@@ -635,6 +636,12 @@ export default class BillPaymentsService implements IBillPaymentsService {
|
||||
]);
|
||||
}
|
||||
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(
|
||||
this.dynamicListService.parseStringifiedFilter
|
||||
)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve bill payment paginted and filterable list.
|
||||
* @param {number} tenantId
|
||||
@@ -642,34 +649,40 @@ export default class BillPaymentsService implements IBillPaymentsService {
|
||||
*/
|
||||
public async listBillPayments(
|
||||
tenantId: number,
|
||||
billPaymentsFilter: IBillPaymentsFilter
|
||||
filterDTO: IBillPaymentsFilter
|
||||
): Promise<{
|
||||
billPayments: IBillPayment;
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
const { BillPayment } = this.tenancy.models(tenantId);
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
|
||||
// Parses filter DTO.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
BillPayment,
|
||||
billPaymentsFilter
|
||||
filter
|
||||
);
|
||||
this.logger.info('[bill_payment] try to get bill payments list.', {
|
||||
tenantId,
|
||||
});
|
||||
|
||||
const { results, pagination } = await BillPayment.query()
|
||||
.onBuild((builder) => {
|
||||
builder.withGraphFetched('vendor');
|
||||
builder.withGraphFetched('paymentAccount');
|
||||
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
dynamicList.buildQuery()(builder);
|
||||
})
|
||||
.pagination(billPaymentsFilter.page - 1, billPaymentsFilter.pageSize);
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
return {
|
||||
billPayments: results,
|
||||
pagination,
|
||||
filterMeta: dynamicFilter.getResponseMeta(),
|
||||
filterMeta: dynamicList.getResponseMeta(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { omit, runInContext, sumBy } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import * as R from 'ramda';
|
||||
import composeAsync from 'async/compose';
|
||||
import {
|
||||
EventDispatcher,
|
||||
@@ -521,6 +522,16 @@ export default class BillsService
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses bills list filter DTO.
|
||||
* @param filterDTO -
|
||||
*/
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(
|
||||
this.dynamicListService.parseStringifiedFilter,
|
||||
)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve bills data table list.
|
||||
* @param {number} tenantId -
|
||||
@@ -528,28 +539,33 @@ export default class BillsService
|
||||
*/
|
||||
public async getBills(
|
||||
tenantId: number,
|
||||
billsFilter: IBillsFilter
|
||||
filterDTO: IBillsFilter
|
||||
): Promise<{
|
||||
bills: IBill;
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
const { Bill } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses bills list filter DTO.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
Bill,
|
||||
billsFilter
|
||||
filter,
|
||||
);
|
||||
this.logger.info('[bills] trying to get bills data table.', {
|
||||
tenantId,
|
||||
billsFilter,
|
||||
filter,
|
||||
});
|
||||
const { results, pagination } = await Bill.query()
|
||||
.onBuild((builder) => {
|
||||
builder.withGraphFetched('vendor');
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
})
|
||||
.pagination(billsFilter.page - 1, billsFilter.pageSize);
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
return {
|
||||
bills: results,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Service, Inject } from 'typedi';
|
||||
import { camelCase, upperFirst } from 'lodash';
|
||||
import pluralize from 'pluralize';
|
||||
import { buildFilter } from 'objection-filter';
|
||||
import { IModel } from 'interfaces';
|
||||
import { IModel, IModelMeta } from 'interfaces';
|
||||
import {
|
||||
getModelFields,
|
||||
} from 'lib/ViewRolesBuilder'
|
||||
@@ -102,4 +102,18 @@ export default class ResourceService {
|
||||
|
||||
return buildFilter(resourceModel).build(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the resource meta.
|
||||
* @param {number} tenantId
|
||||
* @param {string} modelName
|
||||
* @returns {IModelMeta}
|
||||
*/
|
||||
public getResourceMeta(tenantId: number, modelName: string): IModelMeta {
|
||||
const resourceModel = this.getResourceModel(tenantId, modelName);
|
||||
|
||||
const settings = resourceModel.meta();
|
||||
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { omit, sumBy, difference } from 'lodash';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
@@ -613,6 +614,16 @@ export default class PaymentReceiveService implements IPaymentsReceiveService {
|
||||
return saleInvoices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses payments receive list filter DTO.
|
||||
* @param filterDTO
|
||||
*/
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(
|
||||
this.dynamicListService.parseStringifiedFilter
|
||||
)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve payment receives paginated and filterable list.
|
||||
* @param {number} tenantId
|
||||
@@ -620,33 +631,39 @@ export default class PaymentReceiveService implements IPaymentsReceiveService {
|
||||
*/
|
||||
public async listPaymentReceives(
|
||||
tenantId: number,
|
||||
paymentReceivesFilter: IPaymentReceivesFilter
|
||||
filterDTO: IPaymentReceivesFilter
|
||||
): Promise<{
|
||||
paymentReceives: IPaymentReceive[];
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
const { PaymentReceive } = this.tenancy.models(tenantId);
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
|
||||
// Parses filter DTO.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
PaymentReceive,
|
||||
paymentReceivesFilter
|
||||
filter
|
||||
);
|
||||
|
||||
const { results, pagination } = await PaymentReceive.query()
|
||||
.onBuild((builder) => {
|
||||
builder.withGraphFetched('customer');
|
||||
builder.withGraphFetched('depositAccount');
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
dynamicList.buildQuery()(builder);
|
||||
})
|
||||
.pagination(
|
||||
paymentReceivesFilter.page - 1,
|
||||
paymentReceivesFilter.pageSize
|
||||
filter.page - 1,
|
||||
filter.pageSize
|
||||
);
|
||||
|
||||
return {
|
||||
paymentReceives: results,
|
||||
pagination,
|
||||
filterMeta: dynamicFilter.getResponseMeta(),
|
||||
filterMeta: dynamicList.getResponseMeta(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { omit, sumBy } from 'lodash';
|
||||
import { filter, omit, sumBy } from 'lodash';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IEstimatesFilter,
|
||||
IFilterMeta,
|
||||
@@ -412,6 +413,16 @@ export default class SaleEstimateService implements ISalesEstimatesService{
|
||||
return estimate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses estimates list filter DTO.
|
||||
* @param filterDTO
|
||||
*/
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(
|
||||
this.dynamicListService.parseStringifiedFilter
|
||||
)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves estimates filterable and paginated list.
|
||||
* @param {number} tenantId -
|
||||
@@ -419,17 +430,22 @@ export default class SaleEstimateService implements ISalesEstimatesService{
|
||||
*/
|
||||
public async estimatesList(
|
||||
tenantId: number,
|
||||
estimatesFilter: IEstimatesFilter
|
||||
filterDTO: IEstimatesFilter
|
||||
): Promise<{
|
||||
salesEstimates: ISaleEstimate[];
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses filter DTO.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
SaleEstimate,
|
||||
estimatesFilter
|
||||
filter,
|
||||
);
|
||||
|
||||
const { results, pagination } = await SaleEstimate.query()
|
||||
@@ -438,7 +454,7 @@ export default class SaleEstimateService implements ISalesEstimatesService{
|
||||
builder.withGraphFetched('entries');
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
})
|
||||
.pagination(estimatesFilter.page - 1, estimatesFilter.pageSize);
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
return {
|
||||
salesEstimates: results,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { omit, sumBy, join, entries } from 'lodash';
|
||||
import { omit, sumBy } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import moment from 'moment';
|
||||
import composeAsync from 'async/compose';
|
||||
import {
|
||||
@@ -647,6 +648,17 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
||||
return saleInvoice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the sale invoice list filter DTO.
|
||||
* @param filterDTO
|
||||
* @returns
|
||||
*/
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(
|
||||
this.dynamicListService.parseStringifiedFilter,
|
||||
)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve sales invoices filterable and paginated list.
|
||||
* @param {Request} req
|
||||
@@ -655,22 +667,27 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
||||
*/
|
||||
public async salesInvoicesList(
|
||||
tenantId: number,
|
||||
salesInvoicesFilter: ISalesInvoicesFilter
|
||||
filterDTO: ISalesInvoicesFilter
|
||||
): Promise<{
|
||||
salesInvoices: ISaleInvoice[];
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses stringified filter roles.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
SaleInvoice,
|
||||
salesInvoicesFilter
|
||||
filter
|
||||
);
|
||||
|
||||
this.logger.info('[sale_invoice] try to get sales invoices list.', {
|
||||
tenantId,
|
||||
salesInvoicesFilter,
|
||||
filter,
|
||||
});
|
||||
const { results, pagination } = await SaleInvoice.query()
|
||||
.onBuild((builder) => {
|
||||
@@ -678,7 +695,7 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
|
||||
builder.withGraphFetched('customer');
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
})
|
||||
.pagination(salesInvoicesFilter.page - 1, salesInvoicesFilter.pageSize);
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
return {
|
||||
salesInvoices: results,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { omit, sumBy } from 'lodash';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import moment from 'moment';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
@@ -406,6 +407,16 @@ export default class SalesReceiptService implements ISalesReceiptsService {
|
||||
return saleReceipt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the sale receipts list filter DTO.
|
||||
* @param filterDTO
|
||||
*/
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(
|
||||
this.dynamicListService.parseStringifiedFilter,
|
||||
)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve sales receipts paginated and filterable list.
|
||||
* @param {number} tenantId
|
||||
@@ -413,17 +424,22 @@ export default class SalesReceiptService implements ISalesReceiptsService {
|
||||
*/
|
||||
public async salesReceiptsList(
|
||||
tenantId: number,
|
||||
salesReceiptsFilter: ISaleReceiptFilter
|
||||
filterDTO: ISaleReceiptFilter
|
||||
): Promise<{
|
||||
salesReceipts: ISaleReceipt[];
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses the stringified filter roles.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
SaleReceipt,
|
||||
salesReceiptsFilter
|
||||
filter,
|
||||
);
|
||||
|
||||
this.logger.info('[sale_receipt] try to get sales receipts list.', {
|
||||
@@ -437,7 +453,7 @@ export default class SalesReceiptService implements ISalesReceiptsService {
|
||||
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
})
|
||||
.pagination(salesReceiptsFilter.page - 1, salesReceiptsFilter.pageSize);
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
return {
|
||||
salesReceipts: results,
|
||||
|
||||
Reference in New Issue
Block a user