mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 05:40:31 +00:00
feat(contacts): auto-complete contacts.
feat(items): auto-complete items. feat(resources): resource columns feat. feat(contacts): retrieve specific contact details.
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
import { forEach, uniqBy } from 'lodash';
|
||||
import {
|
||||
buildFilterRolesJoins,
|
||||
} from 'lib/ViewRolesBuilder';
|
||||
import { buildFilterRolesJoins } from 'lib/ViewRolesBuilder';
|
||||
import { IModel } from 'interfaces';
|
||||
|
||||
export default class DynamicFilter {
|
||||
@@ -20,7 +18,7 @@ export default class DynamicFilter {
|
||||
|
||||
/**
|
||||
* Set filter.
|
||||
* @param {*} filterRole -
|
||||
* @param {*} filterRole - Filter role.
|
||||
*/
|
||||
setFilter(filterRole) {
|
||||
filterRole.setModel(this.model);
|
||||
@@ -36,14 +34,22 @@ export default class DynamicFilter {
|
||||
|
||||
this.filters.forEach((filter) => {
|
||||
const { filterRoles } = filter;
|
||||
|
||||
buildersCallbacks.push(filter.buildQuery());
|
||||
tableColumns.push(...(Array.isArray(filterRoles)) ? filterRoles : [filterRoles]);
|
||||
tableColumns.push(
|
||||
...(Array.isArray(filterRoles) ? filterRoles : [filterRoles])
|
||||
);
|
||||
});
|
||||
|
||||
return (builder) => {
|
||||
buildersCallbacks.forEach((builderCallback) => {
|
||||
builderCallback(builder);
|
||||
});
|
||||
buildFilterRolesJoins(this.model, uniqBy(tableColumns, 'columnKey'))(builder);
|
||||
|
||||
buildFilterRolesJoins(
|
||||
this.model,
|
||||
uniqBy(tableColumns, 'columnKey')
|
||||
)(builder);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,4 +68,4 @@ export default class DynamicFilter {
|
||||
});
|
||||
return responseMeta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { difference } from 'lodash';
|
||||
import DynamicFilterRoleAbstructor from 'lib/DynamicFilter/DynamicFilterRoleAbstructor';
|
||||
import {
|
||||
buildFilterQuery,
|
||||
} from 'lib/ViewRolesBuilder';
|
||||
import { buildFilterQuery } from 'lib/ViewRolesBuilder';
|
||||
import { IFilterRole } from 'interfaces';
|
||||
|
||||
export default class FilterRoles extends DynamicFilterRoleAbstructor {
|
||||
filterRoles: IFilterRole[];
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {Array} filterRoles -
|
||||
@@ -13,8 +13,9 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor {
|
||||
*/
|
||||
constructor(filterRoles: IFilterRole[]) {
|
||||
super();
|
||||
|
||||
this.filterRoles = filterRoles;
|
||||
this.setResponseMeta();
|
||||
this.setResponseMeta();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,9 +24,10 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor {
|
||||
*/
|
||||
private buildLogicExpression(): string {
|
||||
let expression = '';
|
||||
|
||||
this.filterRoles.forEach((role, index) => {
|
||||
expression += (index === 0) ?
|
||||
`${role.index} ` : `${role.condition} ${role.index} `;
|
||||
expression +=
|
||||
index === 0 ? `${role.index} ` : `${role.condition} ${role.index} `;
|
||||
});
|
||||
return expression.trim();
|
||||
}
|
||||
@@ -45,7 +47,7 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor {
|
||||
*/
|
||||
setResponseMeta() {
|
||||
this.responseMeta = {
|
||||
filterRoles: this.filterRoles
|
||||
filterRoles: this.filterRoles,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import DynamicFilterRoleAbstructor from 'lib/DynamicFilter/DynamicFilterRoleAbstructor';
|
||||
import { getRoleFieldColumn, validateFieldKeyExistance } from 'lib/ViewRolesBuilder';
|
||||
import {
|
||||
getRoleFieldColumn,
|
||||
validateFieldKeyExistance,
|
||||
getTableFromRelationColumn,
|
||||
} from 'lib/ViewRolesBuilder';
|
||||
|
||||
export default class DynamicFilterSortBy extends DynamicFilterRoleAbstructor {
|
||||
sortRole: { fieldKey: string, order: string } = {};
|
||||
sortRole: { fieldKey: string; order: string } = {};
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
@@ -19,6 +23,9 @@ export default class DynamicFilterSortBy extends DynamicFilterRoleAbstructor {
|
||||
this.setResponseMeta();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given field key with the model.
|
||||
*/
|
||||
validate() {
|
||||
validateFieldKeyExistance(this.model, this.sortRole.fieldKey);
|
||||
}
|
||||
@@ -27,15 +34,41 @@ export default class DynamicFilterSortBy extends DynamicFilterRoleAbstructor {
|
||||
* Builds database query of sort by column on the given direction.
|
||||
*/
|
||||
buildQuery() {
|
||||
return (builder) => {
|
||||
const fieldRelation = getRoleFieldColumn(this.model, this.sortRole.fieldKey);
|
||||
const comparatorColumn =
|
||||
fieldRelation.relationColumn ||
|
||||
`${this.tableName}.${fieldRelation.column}`;
|
||||
const fieldRelation = getRoleFieldColumn(
|
||||
this.model,
|
||||
this.sortRole.fieldKey
|
||||
);
|
||||
const comparatorColumn =
|
||||
fieldRelation.relationColumn ||
|
||||
`${this.tableName}.${fieldRelation.column}`;
|
||||
|
||||
if (typeof fieldRelation.sortQuery !== 'undefined') {
|
||||
return (builder) => {
|
||||
fieldRelation.sortQuery(builder, this.sortRole);
|
||||
};
|
||||
}
|
||||
return (builder) => {
|
||||
if (this.sortRole.fieldKey) {
|
||||
builder.orderBy(`${comparatorColumn}`, this.sortRole.order);
|
||||
}
|
||||
this.joinBuildQuery()(builder);
|
||||
};
|
||||
}
|
||||
|
||||
joinBuildQuery() {
|
||||
const fieldColumn = getRoleFieldColumn(this.model, this.sortRole.fieldKey);
|
||||
|
||||
return (builder) => {
|
||||
if (fieldColumn.relation) {
|
||||
const joinTable = getTableFromRelationColumn(fieldColumn.relation);
|
||||
|
||||
builder.join(
|
||||
joinTable,
|
||||
`${this.model.tableName}.${fieldColumn.column}`,
|
||||
'=',
|
||||
fieldColumn.relation
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { omit } from 'lodash';
|
||||
import { IView, IViewRole } from 'interfaces';
|
||||
import DynamicFilterRoleAbstructor from 'lib/DynamicFilter/DynamicFilterRoleAbstructor';
|
||||
import {
|
||||
buildFilterQuery,
|
||||
} from 'lib/ViewRolesBuilder';
|
||||
import { buildFilterQuery } from 'lib/ViewRolesBuilder';
|
||||
|
||||
export default class DynamicFilterViews extends DynamicFilterRoleAbstructor {
|
||||
viewId: number;
|
||||
@@ -12,7 +10,7 @@ export default class DynamicFilterViews extends DynamicFilterRoleAbstructor {
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {IView} view -
|
||||
* @param {IView} view -
|
||||
*/
|
||||
constructor(view: IView) {
|
||||
super();
|
||||
@@ -23,7 +21,7 @@ export default class DynamicFilterViews extends DynamicFilterRoleAbstructor {
|
||||
.replace('AND', '&&')
|
||||
.replace('OR', '||');
|
||||
|
||||
this.setResponseMeta();
|
||||
this.setResponseMeta();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,13 +30,17 @@ export default class DynamicFilterViews extends DynamicFilterRoleAbstructor {
|
||||
buildLogicExpression() {
|
||||
return this.logicExpression;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds database query of view roles.
|
||||
*/
|
||||
buildQuery() {
|
||||
return (builder) => {
|
||||
buildFilterQuery(this.model, this.filterRoles, this.logicExpression)(builder);
|
||||
buildFilterQuery(
|
||||
this.model,
|
||||
this.filterRoles,
|
||||
this.logicExpression
|
||||
)(builder);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -49,11 +51,11 @@ export default class DynamicFilterViews extends DynamicFilterRoleAbstructor {
|
||||
this.responseMeta = {
|
||||
view: {
|
||||
logicExpression: this.logicExpression,
|
||||
filterRoles: this.filterRoles.map((filterRole) =>
|
||||
({ ...omit(filterRole, ['id', 'viewId']) })
|
||||
),
|
||||
filterRoles: this.filterRoles.map((filterRole) => ({
|
||||
...omit(filterRole, ['id', 'viewId']),
|
||||
})),
|
||||
customViewId: this.viewId,
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@ import Parser from 'lib/LogicEvaluation/Parser';
|
||||
import QueryParser from 'lib/LogicEvaluation/QueryParser';
|
||||
import { IFilterRole, IModel } from 'interfaces';
|
||||
|
||||
const numberRoleQueryBuilder = (role: IFilterRole, comparatorColumn: string) => {
|
||||
const numberRoleQueryBuilder = (
|
||||
role: IFilterRole,
|
||||
comparatorColumn: string
|
||||
) => {
|
||||
switch (role.comparator) {
|
||||
case 'equals':
|
||||
case 'equal':
|
||||
@@ -67,28 +70,36 @@ const textRoleQueryBuilder = (role: IFilterRole, comparatorColumn: string) => {
|
||||
};
|
||||
|
||||
const dateQueryBuilder = (role: IFilterRole, comparatorColumn: string) => {
|
||||
switch(role.comparator) {
|
||||
switch (role.comparator) {
|
||||
case 'after':
|
||||
case 'before':
|
||||
return (builder) => {
|
||||
const comparator = role.comparator === 'before' ? '<' : '>';
|
||||
const hasTimeFormat = moment(role.value, 'YYYY-MM-DD HH:MM', true).isValid();
|
||||
const hasTimeFormat = moment(
|
||||
role.value,
|
||||
'YYYY-MM-DD HH:MM',
|
||||
true
|
||||
).isValid();
|
||||
const targetDate = moment(role.value);
|
||||
const dateFormat = 'YYYY-MM-DD HH:MM:SS';
|
||||
|
||||
if (!hasTimeFormat) {
|
||||
if (role.comparator === 'before') {
|
||||
targetDate.startOf('day');
|
||||
targetDate.startOf('day');
|
||||
} else {
|
||||
targetDate.endOf('day');
|
||||
targetDate.endOf('day');
|
||||
}
|
||||
}
|
||||
const comparatorValue = targetDate.format(dateFormat);
|
||||
builder.where(comparatorColumn, comparator, comparatorValue);
|
||||
};
|
||||
case 'in':
|
||||
case 'in':
|
||||
return (builder) => {
|
||||
const hasTimeFormat = moment(role.value, 'YYYY-MM-DD HH:MM', true).isValid();
|
||||
const hasTimeFormat = moment(
|
||||
role.value,
|
||||
'YYYY-MM-DD HH:MM',
|
||||
true
|
||||
).isValid();
|
||||
const dateFormat = 'YYYY-MM-DD HH:MM:SS';
|
||||
|
||||
if (hasTimeFormat) {
|
||||
@@ -112,7 +123,7 @@ const dateQueryBuilder = (role: IFilterRole, comparatorColumn: string) => {
|
||||
*/
|
||||
export function getRoleFieldColumn(model: IModel, fieldKey: string) {
|
||||
const tableFields = model.fields;
|
||||
return (tableFields[fieldKey]) ? tableFields[fieldKey] : null;
|
||||
return tableFields[fieldKey] ? tableFields[fieldKey] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,9 +133,10 @@ export function getRoleFieldColumn(model: IModel, fieldKey: string) {
|
||||
*/
|
||||
export function buildRoleQuery(model: IModel, role: IFilterRole) {
|
||||
const fieldRelation = getRoleFieldColumn(model, role.fieldKey);
|
||||
const comparatorColumn = fieldRelation.relationColumn || `${model.tableName}.${fieldRelation.column}`;
|
||||
const comparatorColumn =
|
||||
fieldRelation.relationColumn ||
|
||||
`${model.tableName}.${fieldRelation.column}`;
|
||||
|
||||
//
|
||||
if (typeof fieldRelation.query !== 'undefined') {
|
||||
return (builder) => {
|
||||
fieldRelation.query(builder, role);
|
||||
@@ -139,7 +151,7 @@ export function buildRoleQuery(model: IModel, role: IFilterRole) {
|
||||
case 'varchar':
|
||||
default:
|
||||
return textRoleQueryBuilder(role, comparatorColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,13 +161,13 @@ export function buildRoleQuery(model: IModel, role: IFilterRole) {
|
||||
*/
|
||||
export const getTableFromRelationColumn = (column: string) => {
|
||||
const splitedColumn = column.split('.');
|
||||
return (splitedColumn.length > 0) ? splitedColumn[0] : '';
|
||||
return splitedColumn.length > 0 ? splitedColumn[0] : '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds view roles join queries.
|
||||
* @param {String} tableName -
|
||||
* @param {Array} roles -
|
||||
* @param {String} tableName - Table name.
|
||||
* @param {Array} roles - Roles.
|
||||
*/
|
||||
export function buildFilterRolesJoins(model: IModel, roles: IFilterRole[]) {
|
||||
return (builder) => {
|
||||
@@ -164,7 +176,13 @@ export function buildFilterRolesJoins(model: IModel, roles: IFilterRole[]) {
|
||||
|
||||
if (fieldColumn.relation) {
|
||||
const joinTable = getTableFromRelationColumn(fieldColumn.relation);
|
||||
builder.join(joinTable, `${model.tableName}.${fieldColumn.column}`, '=', fieldColumn.relation);
|
||||
|
||||
builder.join(
|
||||
joinTable,
|
||||
`${model.tableName}.${fieldColumn.column}`,
|
||||
'=',
|
||||
fieldColumn.relation
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -176,7 +194,12 @@ export function buildSortColumnJoin(model: IModel, sortColumnKey: string) {
|
||||
|
||||
if (fieldColumn.relation) {
|
||||
const joinTable = getTableFromRelationColumn(fieldColumn.relation);
|
||||
builder.join(joinTable, `${model.tableName}.${fieldColumn.column}`, '=', fieldColumn.relation);
|
||||
builder.join(
|
||||
joinTable,
|
||||
`${model.tableName}.${fieldColumn.column}`,
|
||||
'=',
|
||||
fieldColumn.relation
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -187,7 +210,11 @@ export function buildSortColumnJoin(model: IModel, sortColumnKey: string) {
|
||||
* @param {Array} roles -
|
||||
* @return {Function}
|
||||
*/
|
||||
export function buildFilterRolesQuery(model: IModel, roles: IFilterRole[], logicExpression: string = '') {
|
||||
export function buildFilterRolesQuery(
|
||||
model: IModel,
|
||||
roles: IFilterRole[],
|
||||
logicExpression: string = ''
|
||||
) {
|
||||
const rolesIndexSet = {};
|
||||
|
||||
roles.forEach((role) => {
|
||||
@@ -211,7 +238,11 @@ export function buildFilterRolesQuery(model: IModel, roles: IFilterRole[], logic
|
||||
* @param {Array} roles -
|
||||
* @param {String} logicExpression -
|
||||
*/
|
||||
export const buildFilterQuery = (model: IModel, roles: IFilterRole[], logicExpression: string) => {
|
||||
export const buildFilterQuery = (
|
||||
model: IModel,
|
||||
roles: IFilterRole[],
|
||||
logicExpression: string
|
||||
) => {
|
||||
return (builder) => {
|
||||
buildFilterRolesQuery(model, roles, logicExpression)(builder);
|
||||
};
|
||||
@@ -233,7 +264,6 @@ export function mapViewRolesToConditionals(viewRoles) {
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
export function mapFilterRolesToDynamicFilter(roles) {
|
||||
return roles.map((role) => ({
|
||||
...role,
|
||||
@@ -247,32 +277,49 @@ export function mapFilterRolesToDynamicFilter(roles) {
|
||||
* @param {String} columnKey -
|
||||
* @param {String} sortDirection -
|
||||
*/
|
||||
export function buildSortColumnQuery(model: IModel, columnKey: string, sortDirection: string) {
|
||||
export function buildSortColumnQuery(
|
||||
model: IModel,
|
||||
columnKey: string,
|
||||
sortDirection: string
|
||||
) {
|
||||
const fieldRelation = getRoleFieldColumn(model, columnKey);
|
||||
const sortColumn = fieldRelation.relation || `${model.tableName}.${fieldRelation.column}`;
|
||||
const sortColumn =
|
||||
fieldRelation.relation || `${model.tableName}.${fieldRelation.column}`;
|
||||
|
||||
return (builder) => {
|
||||
builder.orderBy(sortColumn, sortDirection);
|
||||
buildSortColumnJoin(model, columnKey)(builder);
|
||||
};
|
||||
}
|
||||
|
||||
export function validateFilterLogicExpression(logicExpression: string, indexes) {
|
||||
|
||||
export function validateFilterLogicExpression(
|
||||
logicExpression: string,
|
||||
indexes
|
||||
) {
|
||||
const logicExpIndexes = logicExpression.match(/\d+/g) || [];
|
||||
const diff = difference(logicExpIndexes.map(Number), indexes);
|
||||
|
||||
return (diff.length > 0) ? false : true;
|
||||
return diff.length > 0 ? false : true;
|
||||
}
|
||||
|
||||
export function validateRolesLogicExpression(logicExpression: string, roles: IFilterRole[]) {
|
||||
return validateFilterLogicExpression(logicExpression, roles.map((r) => r.index));
|
||||
export function validateRolesLogicExpression(
|
||||
logicExpression: string,
|
||||
roles: IFilterRole[]
|
||||
) {
|
||||
return validateFilterLogicExpression(
|
||||
logicExpression,
|
||||
roles.map((r) => r.index)
|
||||
);
|
||||
}
|
||||
|
||||
export function validateFieldKeyExistance(model: any, fieldKey: string) {
|
||||
return model?.fields?.[fieldKey] || false;
|
||||
}
|
||||
|
||||
export function validateFilterRolesFieldsExistance(model, filterRoles: IFilterRole[]) {
|
||||
export function validateFilterRolesFieldsExistance(
|
||||
model,
|
||||
filterRoles: IFilterRole[]
|
||||
) {
|
||||
return filterRoles.filter((filterRole: IFilterRole) => {
|
||||
return !validateFieldKeyExistance(model, filterRole.fieldKey);
|
||||
});
|
||||
@@ -280,15 +327,19 @@ export function validateFilterRolesFieldsExistance(model, filterRoles: IFilterRo
|
||||
|
||||
/**
|
||||
* Retrieve model fields keys.
|
||||
* @param {IModel} Model
|
||||
* @param {IModel} Model
|
||||
* @return {string[]}
|
||||
*/
|
||||
export function getModelFieldsKeys(Model: IModel) {
|
||||
const fields = Object.keys(Model.fields);
|
||||
|
||||
return fields.sort((a, b) => {
|
||||
if (a < b) { return -1; }
|
||||
if (a > b) { return 1; }
|
||||
if (a < b) {
|
||||
return -1;
|
||||
}
|
||||
if (a > b) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
@@ -302,5 +353,5 @@ export function getModelFields(Model: IModel) {
|
||||
...field,
|
||||
key: fieldKey,
|
||||
};
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user