This commit is contained in:
elforjani3
2021-08-07 22:27:08 +02:00
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('inactive_mode').optional().isBoolean().toBoolean(),
query('search_keyword').optional({ nullable: true }).isString().trim(),
];
}

View File

@@ -122,8 +122,9 @@ export default class CustomersController extends ContactsController {
query('stringified_filter_roles').optional().isJSON(),
query('inactive_mode').optional().isBoolean().toBoolean(),
query('search_keyword').optional({ nullable: true }).isString().trim()
];
}
}
/**
* Creates a new customer.

View File

@@ -102,6 +102,7 @@ export default class VendorsController extends ContactsController {
query('page_size').optional().isNumeric().toInt(),
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_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('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('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('column_sort_by').optional(),
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('page').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[] {
return [
query('view_slug').optional({ nullable: true }).isString().trim(),
query('stringified_filter_roles').optional().isJSON(),
query('view_slug').optional({ nullable: true }).isString().trim(),
query('column_sort_by').optional(),
query('sort_order').optional().isIn(['desc', 'asc']),
query('page').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('page').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('page').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('page').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;
sortOrder: string;
stringifiedFilterRoles: string;
searchKeyword?: string;
}
export interface IDynamicListService {
@@ -29,3 +30,9 @@ export interface IDynamicListService {
): Promise<any>;
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 {
page: number;
pageSize: number;
searchKeyword?: string;
}
export interface ISalesInvoicesService {

View File

@@ -1,5 +1,3 @@
import { IModel, IFilterRole } from 'interfaces';
import { FIELD_TYPE } from './constants';
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[]) => {
fieldsRelations.forEach((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;
}
}

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[];
/**
* Constructor method.
* @param {Array} filterRoles -
* @param {Array} resourceFields -
* On initialize filter roles.
*/
constructor(filterRoles: IFilterRole[]) {
super();
this.filterRoles = filterRoles;
this.setResponseMeta();
}
public onInitialize() {
super.onInitialize();
this.setFilterRolesRelations();
}
@@ -27,7 +18,7 @@ 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} `;
@@ -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.
*/

View File

@@ -1,4 +1,5 @@
import moment from 'moment';
import * as R from 'ramda';
import { IFilterRole, IDynamicFilter, IModel } from 'interfaces';
import { Lexer } from 'lib/LogicEvaluation/Lexer';
import Parser from 'lib/LogicEvaluation/Parser';
@@ -63,6 +64,18 @@ export default abstract class DynamicFilterAbstructor
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.
* @param {String} tableName - Table name.
@@ -74,8 +87,10 @@ export default abstract class DynamicFilterAbstructor
roles: IFilterRole[],
logicExpression: string
) => {
const basicExpression = this.parseLogicExpression(logicExpression);
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() {
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 CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Accounts/constants';
import ModelSearchable from './ModelSearchable';
export default class Account extends mixin(TenantModel, [
ModelSettings,
CustomViewBaseModel,
ModelSearchable,
]) {
/**
* Table name.
@@ -257,7 +259,17 @@ export default class Account extends mixin(TenantModel, [
/**
* Retrieve the default custom views, roles and columns.
*/
static get defaultViews() {
static get defaultViews() {
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 CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Purchases/constants';
import ModelSearchable from './ModelSearchable';
export default class Bill extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable
]) {
/**
* Table name
@@ -315,4 +317,15 @@ export default class Bill extends mixin(TenantModel, [
static get defaultViews() {
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 CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Sales/PaymentReceives/constants';
import ModelSearchable from './ModelSearchable';
export default class BillPayment extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable,
]) {
/**
* Table name
@@ -90,4 +92,15 @@ export default class BillPayment extends mixin(TenantModel, [
static get defaultViews() {
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 {
fields: {
first_name: {
name: 'First name',
column: 'first_name',
fieldType: 'text',
},
last_name: {
name: 'Last name',
column: 'last_name',
fieldType: 'text',
},
display_name: {
name: 'Display name',
column: 'display_name',
fieldType: 'text',
columnable: true,
},
email: {
name: 'Email',
column: 'email',
fieldType: 'text',
columnable: true,
},
work_phone: {
name: 'Work phone',
column: 'work_phone',
fieldType: 'text',
columnable: true,
},
personal_phone: {
name: 'Personal phone',
column: 'personal_phone',
fieldType: 'text',
columnable: true,
},
company_name: {
name: 'Company name',
column: 'company_name',
fieldType: 'text',
columnable: true,
},
website: {
name: 'Website',
column: 'website',
fieldType: 'text',
columnable: true,
},
created_at: {
name: 'Created at',
column: 'created_at',
fieldType: 'date',
columnable: true,
},
balance: {
name: 'Balance',
column: 'balance',
fieldType: 'number',
columnable: true,
},
opening_balance: {
name: 'Opening balance',
column: 'opening_balance',
fieldType: 'number',
columnable: true,
},
opening_balance_at: {
name: 'Opening balance at',
column: 'opening_balance_at',
filterable: false,
fieldType: 'date',
columnable: true,
},
currency_code: {
column: 'currency_code',
columnable: true,
fieldType: 'text',
},
status: {
@@ -74,7 +72,6 @@ export default {
{ key: 'overdue', label: 'Overdue' },
{ key: 'unpaid', label: 'Unpaid' },
],
columnable: true,
filterCustomQuery: statusFieldFilterQuery,
},
},

View File

@@ -5,6 +5,7 @@ import ModelSetting from './ModelSetting';
import CustomerSettings from './Customer.Settings';
import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Contacts/Customers/constants';
import ModelSearchable from './ModelSearchable';
class CustomerQueryBuilder extends PaginationQueryBuilder {
constructor(...args) {
@@ -21,6 +22,7 @@ class CustomerQueryBuilder extends PaginationQueryBuilder {
export default class Customer extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable
]) {
/**
* Query builder.
@@ -149,4 +151,20 @@ export default class Customer extends mixin(TenantModel, [
static get defaultViews() {
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 CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Expenses/constants';
import ModelSearchable from './ModelSearchable';
export default class Expense extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable,
]) {
/**
* Table name
@@ -160,4 +162,14 @@ export default class Expense extends mixin(TenantModel, [
static get defaultViews() {
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 CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Items/constants';
import ModelSearchable from './ModelSearchable';
export default class Item extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable,
]) {
/**
* Table name
@@ -128,4 +130,14 @@ export default class Item extends mixin(TenantModel, [
static get defaultViews() {
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 CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/ManualJournals/constants';
import ModelSearchable from './ModelSearchable';
export default class ManualJournal extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable,
]) {
/**
* Table name.
@@ -115,4 +117,15 @@ export default class ManualJournal extends mixin(TenantModel, [
static get defaultViews() {
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 CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Sales/PaymentReceives/constants';
import ModelSearchable from './ModelSearchable';
export default class PaymentReceive extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable
]) {
/**
* Table name.
@@ -94,4 +96,15 @@ export default class PaymentReceive extends mixin(TenantModel, [
static get defaultViews() {
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 CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Sales/Estimates/constants';
import ModelSearchable from './ModelSearchable';
export default class SaleEstimate extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable,
]) {
/**
* Table name
@@ -210,4 +212,15 @@ export default class SaleEstimate extends mixin(TenantModel, [
static get defaultViews() {
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 CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Sales/constants';
import ModelSearchable from './ModelSearchable';
export default class SaleInvoice extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable,
]) {
/**
* Table name
@@ -371,4 +373,22 @@ export default class SaleInvoice extends mixin(TenantModel, [
static get defaultViews() {
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 CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Sales/Receipts/constants';
import ModelSearchable from './ModelSearchable';
export default class SaleReceipt extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable,
]) {
/**
* Table name
@@ -159,4 +161,15 @@ export default class SaleReceipt extends mixin(TenantModel, [
static get defaultViews() {
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',
},
fields: {
first_name: {
name: 'First name',
column: 'first_name',
fieldType: 'text',
},
last_name: {
name: 'Last name',
column: 'last_name',
fieldType: 'text',
},
'display_name': {
name: 'Display name',
column: 'display_name',

View File

@@ -5,6 +5,7 @@ import ModelSetting from './ModelSetting';
import VendorSettings from './Vendor.Settings';
import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from 'services/Contacts/Vendors/constants';
import ModelSearchable from './ModelSearchable';
class VendorQueryBuilder extends PaginationQueryBuilder {
constructor(...args) {
@@ -21,6 +22,7 @@ class VendorQueryBuilder extends PaginationQueryBuilder {
export default class Vendor extends mixin(TenantModel, [
ModelSetting,
CustomViewBaseModel,
ModelSearchable
]) {
/**
* Query builder.
@@ -148,4 +150,20 @@ export default class Vendor extends mixin(TenantModel, [
static get defaultViews() {
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 { IFilterRole, IModel } from 'interfaces';
import DynamicListAbstruct from './DynamicListAbstruct';
import DynamicFilterFilterRoles from 'lib/DynamicFilter/DynamicFilterFilterRoles';
import DynamicFilterAdvancedFilter from 'lib/DynamicFilter/DynamicFilterAdvancedFilter';
import { ERRORS } from './constants';
import { ServiceError } from 'exceptions';
@@ -88,7 +88,7 @@ export default class DynamicListFilterRoles extends DynamicListAbstruct {
public dynamicList = (
model: IModel,
filterRoles: IFilterRole[]
): DynamicFilterFilterRoles => {
): DynamicFilterAdvancedFilter => {
const filterRolesParsed = R.compose(this.incrementFilterRolesIndex)(
filterRoles
);
@@ -98,6 +98,6 @@ export default class DynamicListFilterRoles extends DynamicListAbstruct {
// Validate the model resource fields.
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 DynamicListSortBy from './DynamicListSortBy';
import DynamicListCustomView from './DynamicListCustomView';
import DynamicListSearch from './DynamicListSearch';
@Service()
export default class DynamicListService implements IDynamicListService {
@@ -28,6 +29,9 @@ export default class DynamicListService implements IDynamicListService {
@Inject()
dynamicListView: DynamicListCustomView;
@Inject()
dynamicListSearch: DynamicListSearch;
/**
* Parses filter DTO.
* @param {IMode} model -
@@ -62,6 +66,14 @@ export default class DynamicListService implements IDynamicListService {
// Parses the filter object.
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.
if (filter.viewSlug) {
const dynamicListCustomView =