feat: services quick search.

This commit is contained in:
a.bouhuolia
2021-08-07 20:37:11 +02:00
parent fad9052da8
commit 1b90610cec
39 changed files with 380 additions and 204 deletions

View File

@@ -139,6 +139,7 @@ export default class AccountsController extends BaseController {
query('sort_order').optional().isIn(['desc', 'asc']), query('sort_order').optional().isIn(['desc', 'asc']),
query('inactive_mode').optional().isBoolean().toBoolean(), query('inactive_mode').optional().isBoolean().toBoolean(),
query('search_keyword').optional({ nullable: true }).isString().trim(),
]; ];
} }

View File

@@ -122,6 +122,7 @@ export default class CustomersController extends ContactsController {
query('stringified_filter_roles').optional().isJSON(), query('stringified_filter_roles').optional().isJSON(),
query('inactive_mode').optional().isBoolean().toBoolean(), query('inactive_mode').optional().isBoolean().toBoolean(),
query('search_keyword').optional({ nullable: true }).isString().trim()
]; ];
} }

View File

@@ -102,6 +102,7 @@ export default class VendorsController extends ContactsController {
query('page_size').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(),
query('inactive_mode').optional().isBoolean().toBoolean(), query('inactive_mode').optional().isBoolean().toBoolean(),
query('search_keyword').optional({ nullable: true }).isString().trim()
]; ];
} }

View File

@@ -182,6 +182,8 @@ export default class ExpensesController extends BaseController {
query('page').optional().isNumeric().toInt(), query('page').optional().isNumeric().toInt(),
query('page_size').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(),
query('search_keyword').optional({ nullable: true }).isString().trim(),
]; ];
} }

View File

@@ -187,6 +187,7 @@ export default class ItemsController extends BaseController {
query('stringified_filter_roles').optional().isJSON(), query('stringified_filter_roles').optional().isJSON(),
query('inactive_mode').optional().isBoolean().toBoolean(), query('inactive_mode').optional().isBoolean().toBoolean(),
query('search_keyword').optional({ nullable: true }).isString().trim(),
]; ];
} }

View File

@@ -144,6 +144,7 @@ export default class ManualJournalsController extends BaseController {
query('sort_order').optional().isIn(['desc', 'asc']), query('sort_order').optional().isIn(['desc', 'asc']),
query('stringified_filter_roles').optional().isJSON(), query('stringified_filter_roles').optional().isJSON(),
query('search_keyword').optional({ nullable: true }).isString().trim(),
]; ];
} }

View File

@@ -171,6 +171,7 @@ export default class BillsController extends BaseController {
query('page_size').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(),
query('column_sort_by').optional(), query('column_sort_by').optional(),
query('sort_order').optional().isIn(['desc', 'asc']), query('sort_order').optional().isIn(['desc', 'asc']),
query('search_keyword').optional({ nullable: true }).isString().trim(),
]; ];
} }

View File

@@ -134,6 +134,7 @@ export default class BillsPayments extends BaseController {
query('sort_order').optional().isIn(['desc', 'asc']), query('sort_order').optional().isIn(['desc', 'asc']),
query('page').optional().isNumeric().toInt(), query('page').optional().isNumeric().toInt(),
query('page_size').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(),
query('search_keyword').optional({ nullable: true }).isString().trim(),
]; ];
} }

View File

@@ -115,12 +115,17 @@ export default class PaymentReceivesController extends BaseController {
*/ */
get validatePaymentReceiveList(): ValidationChain[] { get validatePaymentReceiveList(): ValidationChain[] {
return [ return [
query('view_slug').optional({ nullable: true }).isString().trim(),
query('stringified_filter_roles').optional().isJSON(), query('stringified_filter_roles').optional().isJSON(),
query('view_slug').optional({ nullable: true }).isString().trim(),
query('column_sort_by').optional(), query('column_sort_by').optional(),
query('sort_order').optional().isIn(['desc', 'asc']), query('sort_order').optional().isIn(['desc', 'asc']),
query('page').optional().isNumeric().toInt(), query('page').optional().isNumeric().toInt(),
query('page_size').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(),
query('search_keyword').optional({ nullable: true }).isString().trim(),
]; ];
} }

View File

@@ -135,6 +135,7 @@ export default class SalesEstimatesController extends BaseController {
query('sort_order').optional().isIn(['desc', 'asc']), query('sort_order').optional().isIn(['desc', 'asc']),
query('page').optional().isNumeric().toInt(), query('page').optional().isNumeric().toInt(),
query('page_size').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(),
query('search_keyword').optional({ nullable: true }).isString().trim()
]; ];
} }

View File

@@ -135,6 +135,7 @@ export default class SaleInvoicesController extends BaseController {
query('sort_order').optional().isIn(['desc', 'asc']), query('sort_order').optional().isIn(['desc', 'asc']),
query('page').optional().isNumeric().toInt(), query('page').optional().isNumeric().toInt(),
query('page_size').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(),
query('search_keyword').optional({ nullable: true }).isString().trim(),
]; ];
} }

View File

@@ -124,6 +124,7 @@ export default class SalesReceiptsController extends BaseController {
query('sort_order').optional().isIn(['desc', 'asc']), query('sort_order').optional().isIn(['desc', 'asc']),
query('page').optional().isNumeric().toInt(), query('page').optional().isNumeric().toInt(),
query('page_size').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(),
query('search_keyword').optional({ nullable: true }).isString().trim(),
]; ];
} }

View File

@@ -1,81 +0,0 @@
import Container from 'typedi';
import TenancyService from 'services/Tenancy/TenancyService';
exports.up = (knex) => {
const tenancyService = Container.get(TenancyService);
const i18n = tenancyService.i18n(knex.userParams.tenantId);
// Deletes ALL existing entries
return knex('views').del()
.then(() => {
// Inserts seed entries
return knex('views').insert([
// Accounts.
{ id: 15, name: i18n.__('Inactive'), slug: 'inactive', roles_logic_expression: '1', resource_model: 'Account', predefined: true },
{ id: 1, name: i18n.__('Assets'), slug: 'assets', roles_logic_expression: '1', resource_model: 'Account', predefined: true },
{ id: 2, name: i18n.__('Liabilities'), slug: 'liabilities', roles_logic_expression: '1', resource_model: 'Account', predefined: true },
{ id: 3, name: i18n.__('Equity'), slug: 'equity', roles_logic_expression: '1', resource_model: 'Account', predefined: true },
{ id: 4, name: i18n.__('Income'), slug: 'income', roles_logic_expression: '1', resource_model: 'Account', predefined: true },
{ id: 5, name: i18n.__('Expenses'), slug: 'expenses', roles_logic_expression: '1', resource_model: 'Account', predefined: true },
// Items.
{ id: 6, name: i18n.__('Services'), slug: 'services', roles_logic_expression: '1', resource_model: 'Item', predefined: true },
{ id: 7, name: i18n.__('Inventory'), slug: 'inventory', roles_logic_expression: '1', resource_model: 'Item', predefined: true },
{ id: 8, name: i18n.__('Non-Inventory'), slug: 'non-inventory', roles_logic_expression: '1', resource_model: 'Item', predefined: true },
// Manual Journals
{ id: 9, name: i18n.__('Journal'), roles_logic_expression: '1', resource_model: 'ManualJournal', predefined: true },
{ id: 10, name: i18n.__('Credit'), roles_logic_expression: '1', resource_model: 'ManualJournal', predefined: true },
{ id: 11, name: i18n.__('Reconciliation'), roles_logic_expression: '1', resource_model: 'ManualJournal', predefined: true },
// Expenses.
{ id: 12, name: i18n.__('Draft'), slug: 'draft', roles_logic_expression: '1', resource_model: 'Expense', predefined: true, },
{ id: 13, name: i18n.__('Published'), slug: 'published', roles_logic_expression: '1', resource_model: 'Expense', predefined: true, },
// Sales invoices.
{ id: 16, name: i18n.__('Draft'), slug: 'draft', roles_logic_expression: '1', resource_model: 'SaleInvoice', predefined: true, },
{ id: 17, name: i18n.__('Delivered'), slug: 'delivered', roles_logic_expression: '1', resource_model: 'SaleInvoice', predefined: true },
{ id: 18, name: i18n.__('Unpaid'), slug: 'unpaid', roles_logic_expression: '1', resource_model: 'SaleInvoice', predefined: true },
{ id: 19, name: i18n.__('Overdue'), slug: 'overdue', roles_logic_expression: '1', resource_model: 'SaleInvoice', predefined: true },
{ id: 20, name: i18n.__('Partially paid'), slug: 'partially-paid', roles_logic_expression: '1', resource_model: 'SaleInvoice', predefined: true },
{ id: 21, name: i18n.__('Paid'), slug: 'paid', roles_logic_expression: '1', resource_model: 'SaleInvoice', predefined: true },
// Bills.
{ id: 22, name: i18n.__('Draft'), slug: 'draft', roles_logic_expression: '1', resource_model: 'Bill', predefined: true, },
{ id: 23, name: i18n.__('Opened'), slug: 'opened', roles_logic_expression: '1', resource_model: 'Bill', predefined: true },
{ id: 24, name: i18n.__('Unpaid'), slug: 'unpaid', roles_logic_expression: '1', resource_model: 'Bill', predefined: true },
{ id: 25, name: i18n.__('Overdue'), slug: 'overdue', roles_logic_expression: '1', resource_model: 'Bill', predefined: true },
{ id: 26, name: i18n.__('Partially paid'), slug: 'partially-paid', roles_logic_expression: '1', resource_model: 'Bill', predefined: true },
{ id: 27, name: i18n.__('Paid'), slug: 'paid', roles_logic_expression: '1', resource_model: 'Bill', predefined: true },
// Sale estimate.
{ id: 28, name: i18n.__('Draft'), slug: 'draft', roles_logic_expression: '1', resource_model: 'SaleEstimate', predefined: true },
{ id: 29, name: i18n.__('Delivered'), slug: 'delivered', roles_logic_expression: '1', resource_model: 'SaleEstimate', predefined: true },
{ id: 30, name: i18n.__('Approved'), slug: 'approved', roles_logic_expression: '1', resource_model: 'SaleEstimate', predefined: true },
{ id: 31, name: i18n.__('Rejected'), slug: 'rejected', roles_logic_expression: '1', resource_model: 'SaleEstimate', predefined: true },
{ id: 32, name: i18n.__('Invoiced'), slug: 'invoiced', roles_logic_expression: '1', resource_model: 'SaleEstimate', predefined: true },
{ id: 33, name: i18n.__('Expired'), slug: 'expired', roles_logic_expression: '1', resource_model: 'SaleEstimate', predefined: true },
// Sale receipts.
{ id: 34, name: i18n.__('Draft'), slug: 'draft', roles_logic_expression: '1', resource_model: 'SaleReceipt', predefined: true },
{ id: 35, name: i18n.__('Closed'), slug: 'closed', roles_logic_expression: '1', resource_model: 'SaleReceipt', predefined: true },
// Customers
{ id: 36, name: i18n.__('Active'), slug: 'active', roles_logic_expression: '1', resource_model: 'Customer', predefined: true },
{ id: 37, name: i18n.__('Inactive'), slug: 'inactive', roles_logic_expression: '1', resource_model: 'Customer', predefined: true },
{ id: 38, name: i18n.__('Overdue'), slug: 'overdue', roles_logic_expression: '1', resource_model: 'Customer', predefined: true },
{ id: 39, name: i18n.__('Unpaid'), slug: 'inpaid', roles_logic_expression: '1', resource_model: 'Customer', predefined: true },
// Vendors
{ id: 40, name: i18n.__('Active'), slug: 'active', roles_logic_expression: '1', resource_model: 'Vendor', predefined: true },
{ id: 41, name: i18n.__('Inactive'), slug: 'inactive', roles_logic_expression: '1', resource_model: 'Vendor', predefined: true },
{ id: 42, name: i18n.__('Overdue'), slug: 'overdue', roles_logic_expression: '1', resource_model: 'Vendor', predefined: true },
{ id: 43, name: i18n.__('Unpaid'), slug: 'overdue', roles_logic_expression: '1', resource_model: 'Vendor', predefined: true },
]);
});
};
exports.down = (knex) => {
};

View File

@@ -1,75 +0,0 @@
exports.up = (knex) => {
// Deletes ALL existing entries
return knex('view_roles').del()
.then(() => {
// Inserts seed entries
return knex('view_roles').insert([
// Accounts
{ field_key: 'root_type', index: 1, comparator: 'equals', value: 'asset', view_id: 1 },
{ field_key: 'root_type', index: 1, comparator: 'equals', value: 'liability', view_id: 2 },
{ field_key: 'root_type', index: 1, comparator: 'equals', value: 'equity', view_id: 3 },
{ field_key: 'root_type', index: 1, comparator: 'equals', value: 'income', view_id: 4 },
{ field_key: 'root_type', index: 1, comparator: 'equals', value: 'expense', view_id: 5 },
{ field_key: 'active', index: 1, comparator: 'is', value: 1, view_id: 15 },
// Items.
{ field_key: 'type', index: 1, comparator: 'equals', value: 'service', view_id: 6 },
{ field_key: 'type', index: 1, comparator: 'equals', value: 'inventory', view_id: 7 },
{ field_key: 'type', index: 1, comparator: 'equals', value: 'non-inventory', view_id: 8 },
// Expenses.
{ field_key: 'status', index: 1, comparator: 'equals', value: 'draft', view_id: 12 },
{ field_key: 'status', index: 1, comparator: 'equals', value: 'published', view_id: 13 },
// Manual Journals.
{ field_key: 'journal_type', index: 1, comparator: 'equals', value: 'Journal', view_id: 9 },
{ field_key: 'journal_type', index: 1, comparator: 'equals', value: 'CreditNote', view_id: 10 },
{ field_key: 'journal_type', index: 1, comparator: 'equals', value: 'Reconciliation', view_id: 11 },
// Sale invoice.
{ field_key: 'status', index: 1, comparator: 'is', value: 'draft', view_id: 16 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'delivered', view_id: 17 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'unpaid', view_id: 18 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'overdue', view_id: 19 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'partially-paid', view_id: 20 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'paid', view_id: 21 },
// Bills
{ field_key: 'status', index: 1, comparator: 'is', value: 'draft', view_id: 22 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'opened', view_id: 23 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'unpaid', view_id: 24 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'overdue', view_id: 25 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'partially-paid', view_id: 26 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'paid', view_id: 27 },
// Sale estimates
{ field_key: 'status', index: 1, comparator: 'is', value: 'draft', view_id: 28 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'delivered', view_id: 29 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'approved', view_id: 30 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'rejected', view_id: 31 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'invoiced', view_id: 32 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'expired', view_id: 33 },
// Sale receipts.
{ field_key: 'status', index: 1, comparator: 'is', value: 'draft', view_id: 34 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'closed', view_id: 35 },
// Customers
{ field_key: 'status', index: 1, comparator: 'is', value: 'active', view_id: 36 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'inactive', view_id: 37 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'overdue', view_id: 38 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'unpaid', view_id: 39 },
// Vendors
{ field_key: 'status', index: 1, comparator: 'is', value: 'active', view_id: 40 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'inactive', view_id: 41 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'overdue', view_id: 42 },
{ field_key: 'status', index: 1, comparator: 'is', value: 'unpaid', view_id: 43 },
]);
});
};
exports.down = (knex) => {
}

View File

@@ -19,6 +19,7 @@ export interface IDynamicListFilter {
columnSortBy: ISortOrder; columnSortBy: ISortOrder;
sortOrder: string; sortOrder: string;
stringifiedFilterRoles: string; stringifiedFilterRoles: string;
searchKeyword?: string;
} }
export interface IDynamicListService { export interface IDynamicListService {
@@ -29,3 +30,9 @@ export interface IDynamicListService {
): Promise<any>; ): Promise<any>;
handlerErrorsToResponse(error, req, res, next): void; handlerErrorsToResponse(error, req, res, next): void;
} }
// Search role.
export interface ISearchRole {
fieldKey: string;
comparator: string;
}

View File

@@ -40,6 +40,7 @@ export interface ISaleInvoiceEditDTO extends ISaleInvoiceDTO {}
export interface ISalesInvoicesFilter extends IDynamicListFilter { export interface ISalesInvoicesFilter extends IDynamicListFilter {
page: number; page: number;
pageSize: number; pageSize: number;
searchKeyword?: string;
} }
export interface ISalesInvoicesService { export interface ISalesInvoicesService {

View File

@@ -1,5 +1,3 @@
import { IModel, IFilterRole } from 'interfaces';
import { FIELD_TYPE } from './constants';
export default class DynamicFilterAbstructor { export default class DynamicFilterAbstructor {
/** /**
@@ -25,6 +23,11 @@ export default class DynamicFilterAbstructor {
}); });
}; };
/**
* Builds join queries of fields.
* @param builder -
* @param {string[]} fieldsRelations -
*/
private buildFieldsJoinQueries = (builder, fieldsRelations: string[]) => { private buildFieldsJoinQueries = (builder, fieldsRelations: string[]) => {
fieldsRelations.forEach((fieldRelation) => { fieldsRelations.forEach((fieldRelation) => {
const relation = this.model.relationMappings[fieldRelation]; const relation = this.model.relationMappings[fieldRelation];
@@ -38,7 +41,10 @@ export default class DynamicFilterAbstructor {
}); });
}; };
getModel() { /**
* Retrieve the dynamic filter mode.
*/
protected getModel() {
return this.model; return this.model;
} }
} }

View File

@@ -0,0 +1,27 @@
import { IFilterRole } from 'interfaces';
import DynamicFilterFilterRoles from './DynamicFilterFilterRoles';
export default class DynamicFilterAdvancedFilter extends DynamicFilterFilterRoles {
private filterRoles: IFilterRole[];
/**
* Constructor method.
* @param {Array} filterRoles -
* @param {Array} resourceFields -
*/
constructor(filterRoles: IFilterRole[]) {
super();
this.filterRoles = filterRoles;
this.setResponseMeta();
}
/**
* Sets response meta.
*/
private setResponseMeta() {
this.responseMeta = {
filterRoles: this.filterRoles,
};
}
}

View File

@@ -5,19 +5,10 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor {
private filterRoles: IFilterRole[]; private filterRoles: IFilterRole[];
/** /**
* Constructor method. * On initialize filter roles.
* @param {Array} filterRoles -
* @param {Array} resourceFields -
*/ */
constructor(filterRoles: IFilterRole[]) {
super();
this.filterRoles = filterRoles;
this.setResponseMeta();
}
public onInitialize() { public onInitialize() {
super.onInitialize();
this.setFilterRolesRelations(); this.setFilterRolesRelations();
} }
@@ -50,15 +41,6 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor {
}; };
} }
/**
* Sets response meta.
*/
private setResponseMeta() {
this.responseMeta = {
filterRoles: this.filterRoles,
};
}
/** /**
* Sets filter roles relations if field was relation type. * Sets filter roles relations if field was relation type.
*/ */

View File

@@ -1,4 +1,5 @@
import moment from 'moment'; import moment from 'moment';
import * as R from 'ramda';
import { IFilterRole, IDynamicFilter, IModel } from 'interfaces'; import { IFilterRole, IDynamicFilter, IModel } from 'interfaces';
import { Lexer } from 'lib/LogicEvaluation/Lexer'; import { Lexer } from 'lib/LogicEvaluation/Lexer';
import Parser from 'lib/LogicEvaluation/Parser'; import Parser from 'lib/LogicEvaluation/Parser';
@@ -63,6 +64,18 @@ export default abstract class DynamicFilterAbstructor
return queryParser.parse(); return queryParser.parse();
}; };
/**
* Parses the logic expression to base expression.
* @param {string} logicExpression -
* @return {string}
*/
private parseLogicExpression(logicExpression: string): string {
return R.compose(
R.replace(/or|OR/g, '||'),
R.replace(/and|AND/g, '&&'),
)(logicExpression);
}
/** /**
* Builds filter query for query builder. * Builds filter query for query builder.
* @param {String} tableName - Table name. * @param {String} tableName - Table name.
@@ -74,8 +87,10 @@ export default abstract class DynamicFilterAbstructor
roles: IFilterRole[], roles: IFilterRole[],
logicExpression: string logicExpression: string
) => { ) => {
const basicExpression = this.parseLogicExpression(logicExpression);
return (builder) => { return (builder) => {
this.buildFilterRolesQuery(model, roles, logicExpression)(builder); this.buildFilterRolesQuery(model, roles, basicExpression)(builder);
}; };
}; };
@@ -347,11 +362,15 @@ export default abstract class DynamicFilterAbstructor
} }
}; };
/**
* Retrieve the model.
*/
getModel() { getModel() {
return this.model; return this.model;
} }
onInitialize() { /**
* On initialize the registered dynamic filter.
} */
onInitialize() {}
} }

View File

@@ -0,0 +1,48 @@
import { IFilterRole } from 'interfaces';
import DynamicFilterFilterRoles from './DynamicFilterFilterRoles';
export default class DynamicFilterSearch extends DynamicFilterFilterRoles {
private searchKeyword: string;
private filterRoles: IFilterRole[];
/**
* Constructor method.
* @param {string} searchKeyword - Search keyword.
*/
constructor(searchKeyword: string) {
super();
this.searchKeyword = searchKeyword;
}
/**
* On initialize the dynamic filter.
*/
public onInitialize() {
super.onInitialize();
this.filterRoles = this.getModelSearchFilterRoles(this.searchKeyword);
}
/**
* Retrieve the filter roles from model search roles.
* @param {string} searchKeyword
* @returns {IFilterRole[]}
*/
private getModelSearchFilterRoles(searchKeyword: string): IFilterRole[] {
const model = this.getModel();
return model.searchRoles.map((searchRole, index) => ({
...searchRole,
value: searchKeyword,
index: index + 1,
}));
}
/**
*
*/
setResponseMeta() {
this.responseMeta = {
searchKeyword: this.searchKeyword,
};
}
}

View File

@@ -11,10 +11,12 @@ import ModelSettings from './ModelSetting';
import { ACCOUNT_TYPES } from 'data/AccountTypes'; import { ACCOUNT_TYPES } from 'data/AccountTypes';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Accounts/constants'; import { DEFAULT_VIEWS } from 'services/Accounts/constants';
import ModelSearchable from './ModelSearchable';
export default class Account extends mixin(TenantModel, [ export default class Account extends mixin(TenantModel, [
ModelSettings, ModelSettings,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable,
]) { ]) {
/** /**
* Table name. * Table name.
@@ -260,4 +262,14 @@ export default class Account extends mixin(TenantModel, [
static get defaultViews() { static get defaultViews() {
return DEFAULT_VIEWS; return DEFAULT_VIEWS;
} }
/**
* Model search roles.
*/
static get searchRoles() {
return [
{ condition: 'or', fieldKey: 'name', comparator: 'contains' },
{ condition: 'or', fieldKey: 'code', comparator: 'like' },
];
}
} }

View File

@@ -6,10 +6,12 @@ import BillSettings from './Bill.Settings';
import ModelSetting from './ModelSetting'; import ModelSetting from './ModelSetting';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Purchases/constants'; import { DEFAULT_VIEWS } from 'services/Purchases/constants';
import ModelSearchable from './ModelSearchable';
export default class Bill extends mixin(TenantModel, [ export default class Bill extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable
]) { ]) {
/** /**
* Table name * Table name
@@ -315,4 +317,15 @@ export default class Bill extends mixin(TenantModel, [
static get defaultViews() { static get defaultViews() {
return DEFAULT_VIEWS; return DEFAULT_VIEWS;
} }
/**
* Model search attributes.
*/
static get searchRoles() {
return [
{ fieldKey: 'bill_number', comparator: 'contains' },
{ condition: 'or', fieldKey: 'reference_no', comparator: 'contains' },
{ condition: 'or', fieldKey: 'amount', comparator: 'equals' },
];
}
} }

View File

@@ -4,10 +4,12 @@ import ModelSetting from './ModelSetting';
import BillPaymentSettings from './BillPayment.Settings'; import BillPaymentSettings from './BillPayment.Settings';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Sales/PaymentReceives/constants'; import { DEFAULT_VIEWS } from 'services/Sales/PaymentReceives/constants';
import ModelSearchable from './ModelSearchable';
export default class BillPayment extends mixin(TenantModel, [ export default class BillPayment extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable,
]) { ]) {
/** /**
* Table name * Table name
@@ -90,4 +92,15 @@ export default class BillPayment extends mixin(TenantModel, [
static get defaultViews() { static get defaultViews() {
return DEFAULT_VIEWS; return DEFAULT_VIEWS;
} }
/**
* Model search attributes.
*/
static get searchRoles() {
return [
{ fieldKey: 'payment_number', comparator: 'contains' },
{ condition: 'or', fieldKey: 'reference_no', comparator: 'contains' },
{ condition: 'or', fieldKey: 'amount', comparator: 'equals' },
];
}
} }

View File

@@ -1,69 +1,67 @@
export default { export default {
fields: { fields: {
first_name: {
name: 'First name',
column: 'first_name',
fieldType: 'text',
},
last_name: {
name: 'Last name',
column: 'last_name',
fieldType: 'text',
},
display_name: { display_name: {
name: 'Display name', name: 'Display name',
column: 'display_name', column: 'display_name',
fieldType: 'text', fieldType: 'text',
columnable: true,
}, },
email: { email: {
name: 'Email', name: 'Email',
column: 'email', column: 'email',
fieldType: 'text', fieldType: 'text',
columnable: true,
}, },
work_phone: { work_phone: {
name: 'Work phone', name: 'Work phone',
column: 'work_phone', column: 'work_phone',
fieldType: 'text', fieldType: 'text',
columnable: true,
}, },
personal_phone: { personal_phone: {
name: 'Personal phone', name: 'Personal phone',
column: 'personal_phone', column: 'personal_phone',
fieldType: 'text', fieldType: 'text',
columnable: true,
}, },
company_name: { company_name: {
name: 'Company name', name: 'Company name',
column: 'company_name', column: 'company_name',
fieldType: 'text', fieldType: 'text',
columnable: true,
}, },
website: { website: {
name: 'Website', name: 'Website',
column: 'website', column: 'website',
fieldType: 'text', fieldType: 'text',
columnable: true,
}, },
created_at: { created_at: {
name: 'Created at', name: 'Created at',
column: 'created_at', column: 'created_at',
fieldType: 'date',
columnable: true,
}, },
balance: { balance: {
name: 'Balance', name: 'Balance',
column: 'balance', column: 'balance',
fieldType: 'number', fieldType: 'number',
columnable: true,
}, },
opening_balance: { opening_balance: {
name: 'Opening balance', name: 'Opening balance',
column: 'opening_balance', column: 'opening_balance',
fieldType: 'number', fieldType: 'number',
columnable: true,
}, },
opening_balance_at: { opening_balance_at: {
name: 'Opening balance at', name: 'Opening balance at',
column: 'opening_balance_at', column: 'opening_balance_at',
filterable: false, filterable: false,
fieldType: 'date', fieldType: 'date',
columnable: true,
}, },
currency_code: { currency_code: {
column: 'currency_code', column: 'currency_code',
columnable: true,
fieldType: 'text', fieldType: 'text',
}, },
status: { status: {
@@ -74,7 +72,6 @@ export default {
{ key: 'overdue', label: 'Overdue' }, { key: 'overdue', label: 'Overdue' },
{ key: 'unpaid', label: 'Unpaid' }, { key: 'unpaid', label: 'Unpaid' },
], ],
columnable: true,
filterCustomQuery: statusFieldFilterQuery, filterCustomQuery: statusFieldFilterQuery,
}, },
}, },

View File

@@ -5,6 +5,7 @@ import ModelSetting from './ModelSetting';
import CustomerSettings from './Customer.Settings'; import CustomerSettings from './Customer.Settings';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Contacts/Customers/constants'; import { DEFAULT_VIEWS } from 'services/Contacts/Customers/constants';
import ModelSearchable from './ModelSearchable';
class CustomerQueryBuilder extends PaginationQueryBuilder { class CustomerQueryBuilder extends PaginationQueryBuilder {
constructor(...args) { constructor(...args) {
@@ -21,6 +22,7 @@ class CustomerQueryBuilder extends PaginationQueryBuilder {
export default class Customer extends mixin(TenantModel, [ export default class Customer extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable
]) { ]) {
/** /**
* Query builder. * Query builder.
@@ -149,4 +151,20 @@ export default class Customer extends mixin(TenantModel, [
static get defaultViews() { static get defaultViews() {
return DEFAULT_VIEWS; return DEFAULT_VIEWS;
} }
/**
* Model search attributes.
*/
static get searchRoles() {
return [
{ fieldKey: 'display_name', comparator: 'contains' },
{ condition: 'or', fieldKey: 'first_name', comparator: 'contains' },
{ condition: 'or', fieldKey: 'last_name', comparator: 'equals' },
{ condition: 'or', fieldKey: 'company_name', comparator: 'equals' },
{ condition: 'or', fieldKey: 'email', comparator: 'equals' },
{ condition: 'or', fieldKey: 'work_phone', comparator: 'equals' },
{ condition: 'or', fieldKey: 'personal_phone', comparator: 'equals' },
{ condition: 'or', fieldKey: 'website', comparator: 'equals' },
];
}
} }

View File

@@ -5,10 +5,12 @@ import ModelSetting from './ModelSetting';
import ExpenseSettings from './Expense.Settings'; import ExpenseSettings from './Expense.Settings';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Expenses/constants'; import { DEFAULT_VIEWS } from 'services/Expenses/constants';
import ModelSearchable from './ModelSearchable';
export default class Expense extends mixin(TenantModel, [ export default class Expense extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable,
]) { ]) {
/** /**
* Table name * Table name
@@ -160,4 +162,14 @@ export default class Expense extends mixin(TenantModel, [
static get defaultViews() { static get defaultViews() {
return DEFAULT_VIEWS; return DEFAULT_VIEWS;
} }
/**
* Model search attributes.
*/
static get searchRoles() {
return [
{ fieldKey: 'reference_no', comparator: 'contains' },
{ condition: 'or', fieldKey: 'amount', comparator: 'equals' },
];
}
} }

View File

@@ -5,10 +5,12 @@ import ItemSettings from './Item.Settings';
import ModelSetting from './ModelSetting'; import ModelSetting from './ModelSetting';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Items/constants'; import { DEFAULT_VIEWS } from 'services/Items/constants';
import ModelSearchable from './ModelSearchable';
export default class Item extends mixin(TenantModel, [ export default class Item extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable,
]) { ]) {
/** /**
* Table name * Table name
@@ -128,4 +130,14 @@ export default class Item extends mixin(TenantModel, [
static get defaultViews() { static get defaultViews() {
return DEFAULT_VIEWS; return DEFAULT_VIEWS;
} }
/**
* Model search roles.
*/
static get searchRoles() {
return [
{ fieldKey: 'name', comparator: 'contains' },
{ condition: 'or', fieldKey: 'code', comparator: 'like' },
];
}
} }

View File

@@ -5,9 +5,11 @@ import ModelSetting from './ModelSetting';
import ManualJournalSettings from './ManualJournal.Settings'; import ManualJournalSettings from './ManualJournal.Settings';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/ManualJournals/constants'; import { DEFAULT_VIEWS } from 'services/ManualJournals/constants';
import ModelSearchable from './ModelSearchable';
export default class ManualJournal extends mixin(TenantModel, [ export default class ManualJournal extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable,
]) { ]) {
/** /**
* Table name. * Table name.
@@ -115,4 +117,15 @@ export default class ManualJournal extends mixin(TenantModel, [
static get defaultViews() { static get defaultViews() {
return DEFAULT_VIEWS; return DEFAULT_VIEWS;
} }
/**
* Model search attributes.
*/
static get searchRoles() {
return [
{ fieldKey: 'journal_number', comparator: 'contains' },
{ condition: 'or', fieldKey: 'reference', comparator: 'contains' },
{ condition: 'or', fieldKey: 'amount', comparator: 'equals' },
];
}
} }

View File

@@ -0,0 +1,18 @@
import { IModelMeta, ISearchRole } from 'interfaces';
export default (Model) =>
class ModelSearchable extends Model {
/**
* Searchable model.
*/
static get searchable(): IModelMeta {
throw true;
}
/**
* Search roles.
*/
static get searchRoles(): ISearchRole[] {
return [];
}
};

View File

@@ -4,10 +4,12 @@ import ModelSetting from './ModelSetting';
import PaymentReceiveSettings from './PaymentReceive.Settings'; import PaymentReceiveSettings from './PaymentReceive.Settings';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Sales/PaymentReceives/constants'; import { DEFAULT_VIEWS } from 'services/Sales/PaymentReceives/constants';
import ModelSearchable from './ModelSearchable';
export default class PaymentReceive extends mixin(TenantModel, [ export default class PaymentReceive extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable
]) { ]) {
/** /**
* Table name. * Table name.
@@ -94,4 +96,15 @@ export default class PaymentReceive extends mixin(TenantModel, [
static get defaultViews() { static get defaultViews() {
return DEFAULT_VIEWS; return DEFAULT_VIEWS;
} }
/**
* Model search attributes.
*/
static get searchRoles() {
return [
{ fieldKey: 'payment_receive_no', comparator: 'contains' },
{ condition: 'or', fieldKey: 'reference_no', comparator: 'contains' },
{ condition: 'or', fieldKey: 'amount', comparator: 'equals' },
];
}
} }

View File

@@ -6,10 +6,12 @@ import SaleEstimateSettings from './SaleEstimate.Settings';
import ModelSetting from './ModelSetting'; import ModelSetting from './ModelSetting';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Sales/Estimates/constants'; import { DEFAULT_VIEWS } from 'services/Sales/Estimates/constants';
import ModelSearchable from './ModelSearchable';
export default class SaleEstimate extends mixin(TenantModel, [ export default class SaleEstimate extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable,
]) { ]) {
/** /**
* Table name * Table name
@@ -210,4 +212,15 @@ export default class SaleEstimate extends mixin(TenantModel, [
static get defaultViews() { static get defaultViews() {
return DEFAULT_VIEWS; return DEFAULT_VIEWS;
} }
/**
* Model search roles.
*/
static get searchRoles() {
return [
{ fieldKey: 'amount', comparator: 'equals' },
{ condition: 'or', fieldKey: 'estimate_number', comparator: 'contains' },
{ condition: 'or', fieldKey: 'reference_no', comparator: 'contains' },
];
}
} }

View File

@@ -5,10 +5,12 @@ import ModelSetting from './ModelSetting';
import SaleInvoiceMeta from './SaleInvoice.Settings'; import SaleInvoiceMeta from './SaleInvoice.Settings';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Sales/constants'; import { DEFAULT_VIEWS } from 'services/Sales/constants';
import ModelSearchable from './ModelSearchable';
export default class SaleInvoice extends mixin(TenantModel, [ export default class SaleInvoice extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable,
]) { ]) {
/** /**
* Table name * Table name
@@ -371,4 +373,22 @@ export default class SaleInvoice extends mixin(TenantModel, [
static get defaultViews() { static get defaultViews() {
return DEFAULT_VIEWS; return DEFAULT_VIEWS;
} }
/**
* Model searchable.
*/
static get searchable() {
return true;
}
/**
* Model search attributes.
*/
static get searchRoles() {
return [
{ fieldKey: 'invoice_no', comparator: 'contains' },
{ condition: 'or', fieldKey: 'reference_no', comparator: 'contains' },
{ condition: 'or', fieldKey: 'amount', comparator: 'equals' },
];
}
} }

View File

@@ -4,10 +4,12 @@ import ModelSetting from './ModelSetting';
import SaleReceiptSettings from './SaleReceipt.Settings'; import SaleReceiptSettings from './SaleReceipt.Settings';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Sales/Receipts/constants'; import { DEFAULT_VIEWS } from 'services/Sales/Receipts/constants';
import ModelSearchable from './ModelSearchable';
export default class SaleReceipt extends mixin(TenantModel, [ export default class SaleReceipt extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable,
]) { ]) {
/** /**
* Table name * Table name
@@ -159,4 +161,15 @@ export default class SaleReceipt extends mixin(TenantModel, [
static get defaultViews() { static get defaultViews() {
return DEFAULT_VIEWS; return DEFAULT_VIEWS;
} }
/**
* Model search attributes.
*/
static get searchRoles() {
return [
{ fieldKey: 'receipt_number', comparator: 'contains' },
{ condition: 'or', fieldKey: 'reference_no', comparator: 'contains' },
{ condition: 'or', fieldKey: 'amount', comparator: 'equals' },
];
}
} }

View File

@@ -5,6 +5,16 @@ export default {
sortField: 'created_at', sortField: 'created_at',
}, },
fields: { fields: {
first_name: {
name: 'First name',
column: 'first_name',
fieldType: 'text',
},
last_name: {
name: 'Last name',
column: 'last_name',
fieldType: 'text',
},
'display_name': { 'display_name': {
name: 'Display name', name: 'Display name',
column: 'display_name', column: 'display_name',

View File

@@ -5,6 +5,7 @@ import ModelSetting from './ModelSetting';
import VendorSettings from './Vendor.Settings'; import VendorSettings from './Vendor.Settings';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Contacts/Vendors/constants'; import { DEFAULT_VIEWS } from 'services/Contacts/Vendors/constants';
import ModelSearchable from './ModelSearchable';
class VendorQueryBuilder extends PaginationQueryBuilder { class VendorQueryBuilder extends PaginationQueryBuilder {
constructor(...args) { constructor(...args) {
@@ -21,6 +22,7 @@ class VendorQueryBuilder extends PaginationQueryBuilder {
export default class Vendor extends mixin(TenantModel, [ export default class Vendor extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable
]) { ]) {
/** /**
* Query builder. * Query builder.
@@ -148,4 +150,20 @@ export default class Vendor extends mixin(TenantModel, [
static get defaultViews() { static get defaultViews() {
return DEFAULT_VIEWS; return DEFAULT_VIEWS;
} }
/**
* Model search attributes.
*/
static get searchRoles() {
return [
{ fieldKey: 'display_name', comparator: 'contains' },
{ condition: 'or', fieldKey: 'first_name', comparator: 'contains' },
{ condition: 'or', fieldKey: 'last_name', comparator: 'equals' },
{ condition: 'or', fieldKey: 'company_name', comparator: 'equals' },
{ condition: 'or', fieldKey: 'email', comparator: 'equals' },
{ condition: 'or', fieldKey: 'work_phone', comparator: 'equals' },
{ condition: 'or', fieldKey: 'personal_phone', comparator: 'equals' },
{ condition: 'or', fieldKey: 'website', comparator: 'equals' },
];
}
} }

View File

@@ -3,7 +3,7 @@ import * as R from 'ramda';
import validator from 'is-my-json-valid'; import validator from 'is-my-json-valid';
import { IFilterRole, IModel } from 'interfaces'; import { IFilterRole, IModel } from 'interfaces';
import DynamicListAbstruct from './DynamicListAbstruct'; import DynamicListAbstruct from './DynamicListAbstruct';
import DynamicFilterFilterRoles from 'lib/DynamicFilter/DynamicFilterFilterRoles'; import DynamicFilterAdvancedFilter from 'lib/DynamicFilter/DynamicFilterAdvancedFilter';
import { ERRORS } from './constants'; import { ERRORS } from './constants';
import { ServiceError } from 'exceptions'; import { ServiceError } from 'exceptions';
@@ -88,7 +88,7 @@ export default class DynamicListFilterRoles extends DynamicListAbstruct {
public dynamicList = ( public dynamicList = (
model: IModel, model: IModel,
filterRoles: IFilterRole[] filterRoles: IFilterRole[]
): DynamicFilterFilterRoles => { ): DynamicFilterAdvancedFilter => {
const filterRolesParsed = R.compose(this.incrementFilterRolesIndex)( const filterRolesParsed = R.compose(this.incrementFilterRolesIndex)(
filterRoles filterRoles
); );
@@ -98,6 +98,6 @@ export default class DynamicListFilterRoles extends DynamicListAbstruct {
// Validate the model resource fields. // Validate the model resource fields.
this.validateFilterRolesFieldsExistance(model, filterRoles); this.validateFilterRolesFieldsExistance(model, filterRoles);
return new DynamicFilterFilterRoles(filterRolesParsed); return new DynamicFilterAdvancedFilter(filterRolesParsed);
}; };
} }

View File

@@ -0,0 +1,18 @@
import { Service } from 'typedi';
import { IFilterRole, IModel } from 'interfaces';
import DynamicListAbstruct from './DynamicListAbstruct';
import DynamicFilterFilterRoles from 'lib/DynamicFilter/DynamicFilterFilterRoles';
import DynamicFilterSearch from 'lib/DynamicFilter/DynamicFilterSearch';
@Service()
export default class DynamicListSearch extends DynamicListAbstruct {
/**
* Dynamic list filter roles.
* @param {IModel} model
* @param {IFilterRole[]} filterRoles
* @returns {DynamicFilterFilterRoles}
*/
public dynamicSearch = (model: IModel, searchKeyword: string) => {
return new DynamicFilterSearch(searchKeyword);
};
}

View File

@@ -13,6 +13,7 @@ import TenancyService from 'services/Tenancy/TenancyService';
import DynamicListFilterRoles from './DynamicListFilterRoles'; import DynamicListFilterRoles from './DynamicListFilterRoles';
import DynamicListSortBy from './DynamicListSortBy'; import DynamicListSortBy from './DynamicListSortBy';
import DynamicListCustomView from './DynamicListCustomView'; import DynamicListCustomView from './DynamicListCustomView';
import DynamicListSearch from './DynamicListSearch';
@Service() @Service()
export default class DynamicListService implements IDynamicListService { export default class DynamicListService implements IDynamicListService {
@@ -28,6 +29,9 @@ export default class DynamicListService implements IDynamicListService {
@Inject() @Inject()
dynamicListView: DynamicListCustomView; dynamicListView: DynamicListCustomView;
@Inject()
dynamicListSearch: DynamicListSearch;
/** /**
* Parses filter DTO. * Parses filter DTO.
* @param {IMode} model - * @param {IMode} model -
@@ -62,6 +66,14 @@ export default class DynamicListService implements IDynamicListService {
// Parses the filter object. // Parses the filter object.
const parsedFilter = this.parseFilterObject(model, filter); const parsedFilter = this.parseFilterObject(model, filter);
// Search by keyword.
if (filter.searchKeyword) {
const dynamicListSearch = this.dynamicListSearch.dynamicSearch(
model,
filter.searchKeyword
);
dynamicFilter.setFilter(dynamicListSearch);
}
// Custom view filter roles. // Custom view filter roles.
if (filter.viewSlug) { if (filter.viewSlug) {
const dynamicListCustomView = const dynamicListCustomView =