From 2822270ac321ce2e99ce0db8d81675374cad4f16 Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Wed, 4 Aug 2021 15:01:43 +0200 Subject: [PATCH] feat: custom view dynamic filter. --- server/src/api/controllers/Accounts.ts | 5 +- .../src/api/controllers/Contacts/Customers.ts | 2 +- .../src/api/controllers/Contacts/Vendors.ts | 2 +- server/src/api/controllers/Expenses.ts | 2 +- server/src/api/controllers/Items.ts | 2 +- server/src/api/controllers/Purchases/Bills.ts | 2 +- .../api/controllers/Sales/PaymentReceives.ts | 2 +- .../api/controllers/Sales/SalesEstimates.ts | 2 +- .../api/controllers/Sales/SalesInvoices.ts | 2 +- .../api/controllers/Sales/SalesReceipts.ts | 2 +- server/src/interfaces/Expenses.ts | 1 + server/src/interfaces/Item.ts | 1 + server/src/interfaces/View.ts | 3 +- .../DynamicFilter/DynamicFilterAbstructor.ts | 4 + .../DynamicFilterRoleAbstructor.ts | 8 ++ .../lib/DynamicFilter/DynamicFilterViews.ts | 29 +++--- server/src/models/Account.js | 16 +++- server/src/models/Bill.js | 14 ++- server/src/models/CustomViewBaseModel.js | 16 ++++ server/src/models/Customer.Settings.ts | 8 +- server/src/models/Customer.js | 15 ++- server/src/models/Expense.js | 14 ++- server/src/models/Item.js | 14 ++- server/src/models/SaleEstimate.js | 16 +++- server/src/models/SaleInvoice.js | 14 ++- server/src/models/SaleReceipt.js | 16 +++- server/src/models/Vendor.Settings.ts | 10 +- server/src/models/Vendor.js | 27 ++++-- .../src/services/Accounts/AccountsService.ts | 5 +- server/src/services/Accounts/constants.ts | 59 ++++++++++++ .../services/Contacts/Customers/constants.ts | 22 +++++ .../services/Contacts/Vendors/constants.ts | 22 +++++ .../src/services/Contacts/VendorsService.ts | 3 +- server/src/services/Contacts/constants.ts | 22 +++++ .../DynamicListing/DynamicListCustomView.ts | 29 +++--- .../DynamicListing/DynamicListService.ts | 30 +++--- server/src/services/Expenses/constants.ts | 26 +++++ server/src/services/Items/constants.ts | 36 +++++++ server/src/services/Purchases/constants.ts | 64 ++++++++++++- .../src/services/Sales/Estimates/constants.ts | 96 +++++++++++++++++++ .../src/services/Sales/Receipts/constants.ts | 21 ++++ server/src/services/Sales/constants.ts | 60 +++++++++++- 42 files changed, 647 insertions(+), 97 deletions(-) create mode 100644 server/src/models/CustomViewBaseModel.js create mode 100644 server/src/services/Contacts/Customers/constants.ts create mode 100644 server/src/services/Contacts/Vendors/constants.ts create mode 100644 server/src/services/Contacts/constants.ts create mode 100644 server/src/services/Expenses/constants.ts create mode 100644 server/src/services/Sales/Estimates/constants.ts create mode 100644 server/src/services/Sales/Receipts/constants.ts diff --git a/server/src/api/controllers/Accounts.ts b/server/src/api/controllers/Accounts.ts index f89c4d63a..845910ac7 100644 --- a/server/src/api/controllers/Accounts.ts +++ b/server/src/api/controllers/Accounts.ts @@ -127,9 +127,12 @@ export default class AccountsController extends BaseController { return [param('id').exists().isNumeric().toInt()]; } + /** + * Accounts list validation schema. + */ get accountsListSchema() { return [ - query('custom_view_id').optional().isNumeric().toInt(), + query('view_slug').optional({ nullable: true }).isString().trim(), query('stringified_filter_roles').optional().isJSON(), query('column_sort_by').optional(), diff --git a/server/src/api/controllers/Contacts/Customers.ts b/server/src/api/controllers/Contacts/Customers.ts index 3b7e5734f..01e0c1020 100644 --- a/server/src/api/controllers/Contacts/Customers.ts +++ b/server/src/api/controllers/Contacts/Customers.ts @@ -118,7 +118,7 @@ export default class CustomersController extends ContactsController { query('page').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(), - query('custom_view_id').optional().isNumeric().toInt(), + query('view_slug').optional().isString().trim(), query('stringified_filter_roles').optional().isJSON(), query('inactive_mode').optional().isBoolean().toBoolean(), diff --git a/server/src/api/controllers/Contacts/Vendors.ts b/server/src/api/controllers/Contacts/Vendors.ts index 53ad0e61a..f0707bea3 100644 --- a/server/src/api/controllers/Contacts/Vendors.ts +++ b/server/src/api/controllers/Contacts/Vendors.ts @@ -92,7 +92,7 @@ export default class VendorsController extends ContactsController { */ get vendorsListSchema() { return [ - query('custom_view_id').optional().isNumeric().toInt(), + query('view_slug').optional().isString().trim(), query('stringified_filter_roles').optional().isJSON(), query('column_sort_by').optional(), diff --git a/server/src/api/controllers/Expenses.ts b/server/src/api/controllers/Expenses.ts index 5740dc316..25a7084dd 100644 --- a/server/src/api/controllers/Expenses.ts +++ b/server/src/api/controllers/Expenses.ts @@ -174,7 +174,7 @@ export default class ExpensesController extends BaseController { */ get expensesListSchema() { return [ - query('custom_view_id').optional().isNumeric().toInt(), + query('view_slug').optional({ nullable: true }).isString().trim(), query('stringified_filter_roles').optional().isJSON(), query('column_sort_by').optional(), diff --git a/server/src/api/controllers/Items.ts b/server/src/api/controllers/Items.ts index d4b4432cb..76161095d 100644 --- a/server/src/api/controllers/Items.ts +++ b/server/src/api/controllers/Items.ts @@ -183,7 +183,7 @@ export default class ItemsController extends BaseController { query('page').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(), - query('custom_view_id').optional().isNumeric().toInt(), + query('view_slug').optional({ nullable: true }).isString().trim(), query('stringified_filter_roles').optional().isJSON(), query('inactive_mode').optional().isBoolean().toBoolean(), diff --git a/server/src/api/controllers/Purchases/Bills.ts b/server/src/api/controllers/Purchases/Bills.ts index 93664aee4..17cf4a9b6 100644 --- a/server/src/api/controllers/Purchases/Bills.ts +++ b/server/src/api/controllers/Purchases/Bills.ts @@ -165,7 +165,7 @@ export default class BillsController extends BaseController { */ get billsListingValidationSchema() { return [ - query('custom_view_id').optional().isNumeric().toInt(), + query('view_slug').optional().isString().trim(), query('stringified_filter_roles').optional().isJSON(), query('page').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(), diff --git a/server/src/api/controllers/Sales/PaymentReceives.ts b/server/src/api/controllers/Sales/PaymentReceives.ts index aad0dad9b..e308cc724 100644 --- a/server/src/api/controllers/Sales/PaymentReceives.ts +++ b/server/src/api/controllers/Sales/PaymentReceives.ts @@ -115,7 +115,7 @@ export default class PaymentReceivesController extends BaseController { */ get validatePaymentReceiveList(): ValidationChain[] { return [ - query('custom_view_id').optional().isNumeric().toInt(), + query('view_slug').optional({ nullable: true }).isString().trim(), query('stringified_filter_roles').optional().isJSON(), query('column_sort_by').optional(), query('sort_order').optional().isIn(['desc', 'asc']), diff --git a/server/src/api/controllers/Sales/SalesEstimates.ts b/server/src/api/controllers/Sales/SalesEstimates.ts index f723980d2..3d75379f5 100644 --- a/server/src/api/controllers/Sales/SalesEstimates.ts +++ b/server/src/api/controllers/Sales/SalesEstimates.ts @@ -129,7 +129,7 @@ export default class SalesEstimatesController extends BaseController { */ get validateEstimateListSchema() { return [ - query('custom_view_id').optional().isNumeric().toInt(), + query('view_slug').optional().isString().trim(), query('stringified_filter_roles').optional().isJSON(), query('column_sort_by').optional(), query('sort_order').optional().isIn(['desc', 'asc']), diff --git a/server/src/api/controllers/Sales/SalesInvoices.ts b/server/src/api/controllers/Sales/SalesInvoices.ts index df04fe5c0..37d6cdf51 100644 --- a/server/src/api/controllers/Sales/SalesInvoices.ts +++ b/server/src/api/controllers/Sales/SalesInvoices.ts @@ -129,7 +129,7 @@ export default class SaleInvoicesController extends BaseController { */ get saleInvoiceListValidationSchema() { return [ - query('custom_view_id').optional().isNumeric().toInt(), + query('view_slug').optional({ nullable: true }).isString().trim(), query('stringified_filter_roles').optional().isJSON(), query('column_sort_by').optional(), query('sort_order').optional().isIn(['desc', 'asc']), diff --git a/server/src/api/controllers/Sales/SalesReceipts.ts b/server/src/api/controllers/Sales/SalesReceipts.ts index 9c621e7c0..ed7dd80bf 100644 --- a/server/src/api/controllers/Sales/SalesReceipts.ts +++ b/server/src/api/controllers/Sales/SalesReceipts.ts @@ -118,7 +118,7 @@ export default class SalesReceiptsController extends BaseController { */ get listSalesReceiptsValidationSchema() { return [ - query('custom_view_id').optional().isNumeric().toInt(), + query('view_slug').optional().isString().trim(), query('stringified_filter_roles').optional().isJSON(), query('column_sort_by').optional(), query('sort_order').optional().isIn(['desc', 'asc']), diff --git a/server/src/interfaces/Expenses.ts b/server/src/interfaces/Expenses.ts index 78dba8946..3cd6d2f87 100644 --- a/server/src/interfaces/Expenses.ts +++ b/server/src/interfaces/Expenses.ts @@ -13,6 +13,7 @@ export interface IExpensesFilter { filterRoles?: IFilterRole[]; columnSortBy: string; sortOrder: string; + viewSlug?: string; } export interface IExpense { diff --git a/server/src/interfaces/Item.ts b/server/src/interfaces/Item.ts index f9b9800d5..981445cff 100644 --- a/server/src/interfaces/Item.ts +++ b/server/src/interfaces/Item.ts @@ -73,6 +73,7 @@ export interface IItemsFilter extends IDynamicListFilterDTO { page: number, pageSize: number, inactiveMode: boolean, + viewSlug?: string, }; export interface IItemsAutoCompleteFilter { diff --git a/server/src/interfaces/View.ts b/server/src/interfaces/View.ts index 06197cae8..39c32190c 100644 --- a/server/src/interfaces/View.ts +++ b/server/src/interfaces/View.ts @@ -2,6 +2,7 @@ export interface IView { id: number, name: string, + slug: string; predefined: boolean, resourceModel: string, favourite: boolean, @@ -13,7 +14,7 @@ export interface IView { export interface IViewRole { id: number, - fieldId: number, + fieldKey: string, index: number, comparator: string, value: string, diff --git a/server/src/lib/DynamicFilter/DynamicFilterAbstructor.ts b/server/src/lib/DynamicFilter/DynamicFilterAbstructor.ts index 17f2afad7..45abe81e1 100644 --- a/server/src/lib/DynamicFilter/DynamicFilterAbstructor.ts +++ b/server/src/lib/DynamicFilter/DynamicFilterAbstructor.ts @@ -37,4 +37,8 @@ export default class DynamicFilterAbstructor { } }); }; + + getModel() { + return this.model; + } } diff --git a/server/src/lib/DynamicFilter/DynamicFilterRoleAbstructor.ts b/server/src/lib/DynamicFilter/DynamicFilterRoleAbstructor.ts index 25ee0c340..83bc2566f 100644 --- a/server/src/lib/DynamicFilter/DynamicFilterRoleAbstructor.ts +++ b/server/src/lib/DynamicFilter/DynamicFilterRoleAbstructor.ts @@ -346,4 +346,12 @@ export default abstract class DynamicFilterAbstructor this.relationFields.push(field.relationKey); } }; + + getModel() { + return this.model; + } + + onInitialize() { + + } } diff --git a/server/src/lib/DynamicFilter/DynamicFilterViews.ts b/server/src/lib/DynamicFilter/DynamicFilterViews.ts index 060efa5f5..c3fe2f3b1 100644 --- a/server/src/lib/DynamicFilter/DynamicFilterViews.ts +++ b/server/src/lib/DynamicFilter/DynamicFilterViews.ts @@ -1,12 +1,12 @@ import { omit } from 'lodash'; import { IView, IViewRole } from 'interfaces'; -import DynamicFilterRoleAbstructor from 'lib/DynamicFilter/DynamicFilterRoleAbstructor'; -import { buildFilterQuery } from 'lib/ViewRolesBuilder'; +import DynamicFilterRoleAbstructor from './DynamicFilterRoleAbstructor'; export default class DynamicFilterViews extends DynamicFilterRoleAbstructor { - viewId: number; - logicExpression: string; - filterRoles: IViewRole[]; + private viewSlug: string; + private logicExpression: string; + private filterRoles: IViewRole[]; + private viewColumns = []; /** * Constructor method. @@ -15,8 +15,9 @@ export default class DynamicFilterViews extends DynamicFilterRoleAbstructor { constructor(view: IView) { super(); - this.viewId = view.id; + this.viewSlug = view.slug; this.filterRoles = view.roles; + this.viewColumns = view.columns; this.logicExpression = view.rolesLogicExpression .replace('AND', '&&') .replace('OR', '||'); @@ -24,19 +25,12 @@ export default class DynamicFilterViews extends DynamicFilterRoleAbstructor { this.setResponseMeta(); } - /** - * Retrieve logic expression. - */ - buildLogicExpression() { - return this.logicExpression; - } - /** * Builds database query of view roles. */ - buildQuery() { + public buildQuery() { return (builder) => { - buildFilterQuery( + this.buildFilterQuery( this.model, this.filterRoles, this.logicExpression @@ -47,14 +41,15 @@ export default class DynamicFilterViews extends DynamicFilterRoleAbstructor { /** * Sets response meta. */ - setResponseMeta() { + public setResponseMeta() { this.responseMeta = { view: { logicExpression: this.logicExpression, filterRoles: this.filterRoles.map((filterRole) => ({ ...omit(filterRole, ['id', 'viewId']), })), - customViewId: this.viewId, + viewSlug: this.viewSlug, + viewColumns: this.viewColumns, }, }; } diff --git a/server/src/models/Account.js b/server/src/models/Account.js index 29e5ad1f8..3df685151 100644 --- a/server/src/models/Account.js +++ b/server/src/models/Account.js @@ -9,8 +9,13 @@ import AccountTypesUtils from 'lib/AccountTypes'; import AccountSettings from './Account.Settings'; import ModelSettings from './ModelSetting'; import { ACCOUNT_TYPES } from 'data/AccountTypes'; +import CustomViewBaseModel from './CustomViewBaseModel'; +import { DEFAULT_VIEWS } from 'services/Accounts/constants'; -export default class Account extends mixin(TenantModel, [ModelSettings]) { +export default class Account extends mixin(TenantModel, [ + ModelSettings, + CustomViewBaseModel, +]) { /** * Table name. */ @@ -135,7 +140,7 @@ export default class Account extends mixin(TenantModel, [ModelSettings]) { */ filterByAccountNormal(query, accountNormal) { const filterTypes = ACCOUNT_TYPES.filter( - (accountType) => accountType.normal === accountNormal, + (accountType) => accountType.normal === accountNormal ).map((accountType) => accountType.key); query.whereIn('account_type', filterTypes); @@ -248,4 +253,11 @@ export default class Account extends mixin(TenantModel, [ModelSettings]) { static get meta() { return AccountSettings; } + + /** + * Retrieve the default custom views, roles and columns. + */ + static get defaultViews() { + return DEFAULT_VIEWS; + } } diff --git a/server/src/models/Bill.js b/server/src/models/Bill.js index 61a354e53..52150454c 100644 --- a/server/src/models/Bill.js +++ b/server/src/models/Bill.js @@ -4,8 +4,13 @@ import { difference } from 'lodash'; import TenantModel from 'models/TenantModel'; import BillSettings from './Bill.Settings'; import ModelSetting from './ModelSetting'; +import CustomViewBaseModel from './CustomViewBaseModel'; +import { DEFAULT_VIEWS } from 'services/Purchases/constants'; -export default class Bill extends mixin(TenantModel, [ModelSetting]) { +export default class Bill extends mixin(TenantModel, [ + ModelSetting, + CustomViewBaseModel, +]) { /** * Table name */ @@ -303,4 +308,11 @@ export default class Bill extends mixin(TenantModel, [ModelSetting]) { .where('id', billId) [changeMethod]('payment_amount', Math.abs(amount)); } + + /** + * Retrieve the default custom views, roles and columns. + */ + static get defaultViews() { + return DEFAULT_VIEWS; + } } diff --git a/server/src/models/CustomViewBaseModel.js b/server/src/models/CustomViewBaseModel.js new file mode 100644 index 000000000..c5a2ee934 --- /dev/null +++ b/server/src/models/CustomViewBaseModel.js @@ -0,0 +1,16 @@ +export default (Model) => + class CustomViewBaseModel extends Model { + /** + * Retrieve the default custom views, roles and columns. + */ + static get defaultViews() { + return []; + } + + /** + * Retrieve the default view by the given slug. + */ + static getDefaultViewBySlug(viewSlug) { + return this.defaultViews.find((view) => view.slug === viewSlug) || null; + } + }; diff --git a/server/src/models/Customer.Settings.ts b/server/src/models/Customer.Settings.ts index ff466ae62..ec517c23d 100644 --- a/server/src/models/Customer.Settings.ts +++ b/server/src/models/Customer.Settings.ts @@ -75,19 +75,13 @@ export default { { key: 'unpaid', label: 'Unpaid' }, ], columnable: true, - filterQuery: statusFieldFilterQuery, + filterCustomQuery: statusFieldFilterQuery, }, }, }; function statusFieldFilterQuery(query, role) { switch (role.value) { - case 'active': - query.modify('active'); - break; - case 'inactive': - query.modify('inactive'); - break; case 'overdue': query.modify('overdue'); break; diff --git a/server/src/models/Customer.js b/server/src/models/Customer.js index 4943c2777..6692a0aef 100644 --- a/server/src/models/Customer.js +++ b/server/src/models/Customer.js @@ -1,9 +1,10 @@ import { Model, mixin } from 'objection'; import TenantModel from 'models/TenantModel'; import PaginationQueryBuilder from './Pagination'; -import QueryParser from 'lib/LogicEvaluation/QueryParser'; import ModelSetting from './ModelSetting'; import CustomerSettings from './Customer.Settings'; +import CustomViewBaseModel from './CustomViewBaseModel'; +import { DEFAULT_VIEWS } from 'services/Contacts/Customers/constants'; class CustomerQueryBuilder extends PaginationQueryBuilder { constructor(...args) { @@ -17,7 +18,10 @@ class CustomerQueryBuilder extends PaginationQueryBuilder { } } -export default class Customer extends mixin(TenantModel, [ModelSetting]) { +export default class Customer extends mixin(TenantModel, [ + ModelSetting, + CustomViewBaseModel, +]) { /** * Query builder. */ @@ -138,4 +142,11 @@ export default class Customer extends mixin(TenantModel, [ModelSetting]) { static get meta() { return CustomerSettings; } + + /** + * Retrieve the default custom views, roles and columns. + */ + static get defaultViews() { + return DEFAULT_VIEWS; + } } diff --git a/server/src/models/Expense.js b/server/src/models/Expense.js index fc8afbcd5..041f9046b 100644 --- a/server/src/models/Expense.js +++ b/server/src/models/Expense.js @@ -3,8 +3,13 @@ import TenantModel from 'models/TenantModel'; import { viewRolesBuilder } from 'lib/ViewRolesBuilder'; import ModelSetting from './ModelSetting'; import ExpenseSettings from './Expense.Settings'; +import CustomViewBaseModel from './CustomViewBaseModel'; +import { DEFAULT_VIEWS } from 'services/Expenses/constants'; -export default class Expense extends mixin(TenantModel, [ModelSetting]) { +export default class Expense extends mixin(TenantModel, [ + ModelSetting, + CustomViewBaseModel, +]) { /** * Table name */ @@ -148,4 +153,11 @@ export default class Expense extends mixin(TenantModel, [ModelSetting]) { static get meta() { return ExpenseSettings; } + + /** + * Retrieve the default custom views, roles and columns. + */ + static get defaultViews() { + return DEFAULT_VIEWS; + } } diff --git a/server/src/models/Item.js b/server/src/models/Item.js index 9ab191bf1..12e661da7 100644 --- a/server/src/models/Item.js +++ b/server/src/models/Item.js @@ -3,8 +3,13 @@ import TenantModel from 'models/TenantModel'; import { buildFilterQuery } from 'lib/ViewRolesBuilder'; import ItemSettings from './Item.Settings'; import ModelSetting from './ModelSetting'; +import CustomViewBaseModel from './CustomViewBaseModel'; +import { DEFAULT_VIEWS } from 'services/Items/constants'; -export default class Item extends mixin(TenantModel, [ModelSetting]) { +export default class Item extends mixin(TenantModel, [ + ModelSetting, + CustomViewBaseModel, +]) { /** * Table name */ @@ -116,4 +121,11 @@ export default class Item extends mixin(TenantModel, [ModelSetting]) { static get meta() { return ItemSettings; } + + /** + * Retrieve the default custom views, roles and columns. + */ + static get defaultViews() { + return DEFAULT_VIEWS; + } } diff --git a/server/src/models/SaleEstimate.js b/server/src/models/SaleEstimate.js index 473027af3..6df889b28 100644 --- a/server/src/models/SaleEstimate.js +++ b/server/src/models/SaleEstimate.js @@ -4,8 +4,13 @@ import TenantModel from 'models/TenantModel'; import { defaultToTransform } from 'utils'; import SaleEstimateSettings from './SaleEstimate.Settings'; import ModelSetting from './ModelSetting'; +import CustomViewBaseModel from './CustomViewBaseModel'; +import { DEFAULT_VIEWS } from 'services/Sales/Estimates/constants'; -export default class SaleEstimate extends mixin(TenantModel, [ModelSetting]) { +export default class SaleEstimate extends mixin(TenantModel, [ + ModelSetting, + CustomViewBaseModel, +]) { /** * Table name */ @@ -155,7 +160,7 @@ export default class SaleEstimate extends mixin(TenantModel, [ModelSetting]) { query.modify('expired'); break; } - } + }, }; } @@ -198,4 +203,11 @@ export default class SaleEstimate extends mixin(TenantModel, [ModelSetting]) { static get meta() { return SaleEstimateSettings; } + + /** + * Retrieve the default custom views, roles and columns. + */ + static get defaultViews() { + return DEFAULT_VIEWS; + } } diff --git a/server/src/models/SaleInvoice.js b/server/src/models/SaleInvoice.js index 5abb5d005..bec1be16a 100644 --- a/server/src/models/SaleInvoice.js +++ b/server/src/models/SaleInvoice.js @@ -3,8 +3,13 @@ import moment from 'moment'; import TenantModel from 'models/TenantModel'; import ModelSetting from './ModelSetting'; import SaleInvoiceMeta from './SaleInvoice.Settings'; +import CustomViewBaseModel from './CustomViewBaseModel'; +import { DEFAULT_VIEWS } from 'services/Sales/constants'; -export default class SaleInvoice extends mixin(TenantModel, [ModelSetting]) { +export default class SaleInvoice extends mixin(TenantModel, [ + ModelSetting, + CustomViewBaseModel, +]) { /** * Table name */ @@ -359,4 +364,11 @@ export default class SaleInvoice extends mixin(TenantModel, [ModelSetting]) { static dueAmountFieldSortQuery(query, role) { query.modify('sortByDueAmount', role.order); } + + /** + * Retrieve the default custom views, roles and columns. + */ + static get defaultViews() { + return DEFAULT_VIEWS; + } } diff --git a/server/src/models/SaleReceipt.js b/server/src/models/SaleReceipt.js index 223565e36..85a0a945d 100644 --- a/server/src/models/SaleReceipt.js +++ b/server/src/models/SaleReceipt.js @@ -2,8 +2,13 @@ import { Model, mixin } from 'objection'; import TenantModel from 'models/TenantModel'; import ModelSetting from './ModelSetting'; import SaleReceiptSettings from './SaleReceipt.Settings'; +import CustomViewBaseModel from './CustomViewBaseModel'; +import { DEFAULT_VIEWS } from 'services/Sales/Receipts/constants'; -export default class SaleReceipt extends mixin(TenantModel, [ModelSetting]) { +export default class SaleReceipt extends mixin(TenantModel, [ + ModelSetting, + CustomViewBaseModel, +]) { /** * Table name */ @@ -80,7 +85,7 @@ export default class SaleReceipt extends mixin(TenantModel, [ModelSetting]) { query.modify('closed'); break; } - } + }, }; } @@ -147,4 +152,11 @@ export default class SaleReceipt extends mixin(TenantModel, [ModelSetting]) { static get meta() { return SaleReceiptSettings; } + + /** + * Retrieve the default custom views, roles and columns. + */ + static get defaultViews() { + return DEFAULT_VIEWS; + } } diff --git a/server/src/models/Vendor.Settings.ts b/server/src/models/Vendor.Settings.ts index 8496cc0f5..18443316a 100644 --- a/server/src/models/Vendor.Settings.ts +++ b/server/src/models/Vendor.Settings.ts @@ -63,19 +63,11 @@ export default { 'status': { label: 'Status', options: [ - { key: 'active', label: 'Active' }, - { key: 'inactive', label: 'Inactive' }, { key: 'overdue', label: 'Overdue' }, { key: 'unpaid', label: 'Unpaid' }, ], - query: (query, role) => { + filterCustomQuery: (query, role) => { switch (role.value) { - case 'active': - query.modify('active'); - break; - case 'inactive': - query.modify('inactive'); - break; case 'overdue': query.modify('overdue'); break; diff --git a/server/src/models/Vendor.js b/server/src/models/Vendor.js index bf4f16bdb..1f8210002 100644 --- a/server/src/models/Vendor.js +++ b/server/src/models/Vendor.js @@ -3,6 +3,8 @@ import TenantModel from 'models/TenantModel'; import PaginationQueryBuilder from './Pagination'; import ModelSetting from './ModelSetting'; import VendorSettings from './Vendor.Settings'; +import CustomViewBaseModel from './CustomViewBaseModel'; +import { DEFAULT_VIEWS } from 'services/Contacts/Vendors/constants'; class VendorQueryBuilder extends PaginationQueryBuilder { constructor(...args) { @@ -16,7 +18,10 @@ class VendorQueryBuilder extends PaginationQueryBuilder { } } -export default class Vendor extends mixin(TenantModel, [ModelSetting]) { +export default class Vendor extends mixin(TenantModel, [ + ModelSetting, + CustomViewBaseModel, +]) { /** * Query builder. */ @@ -67,7 +72,7 @@ export default class Vendor extends mixin(TenantModel, [ModelSetting]) { /** * Inactive/Active mode. */ - inactiveMode(query, active = false) { + inactiveMode(query, active = false) { query.where('active', !active); }, @@ -89,10 +94,9 @@ export default class Vendor extends mixin(TenantModel, [ModelSetting]) { overdue(query) { query.select( '*', - Vendor - .relatedQuery('overdueBills', query.knex()) + Vendor.relatedQuery('overdueBills', query.knex()) .count() - .as('countOverdue'), + .as('countOverdue') ); query.having('countOverdue', '>', 0); }, @@ -101,7 +105,7 @@ export default class Vendor extends mixin(TenantModel, [ModelSetting]) { */ unpaid(query) { query.whereRaw('`BALANCE` + `OPENING_BALANCE` <> 0'); - } + }, }; } @@ -129,12 +133,19 @@ export default class Vendor extends mixin(TenantModel, [ModelSetting]) { }, filter: (query) => { query.modify('overdue'); - } - } + }, + }, }; } static get meta() { return VendorSettings; } + + /** + * Retrieve the default custom views, roles and columns. + */ + static get defaultViews() { + return DEFAULT_VIEWS; + } } diff --git a/server/src/services/Accounts/AccountsService.ts b/server/src/services/Accounts/AccountsService.ts index 0f56e122f..e3f97b300 100644 --- a/server/src/services/Accounts/AccountsService.ts +++ b/server/src/services/Accounts/AccountsService.ts @@ -638,10 +638,7 @@ export default class AccountsService { Account, filter ); - this.logger.info('[accounts] trying to get accounts datatable list.', { - tenantId, - filter, - }); + // Retrieve accounts model based on the given query. const accounts = await Account.query().onBuild((builder) => { dynamicList.buildQuery()(builder); builder.modify('inactiveMode', filter.inactiveMode); diff --git a/server/src/services/Accounts/constants.ts b/server/src/services/Accounts/constants.ts index 13ff9cc34..e4fefc2c8 100644 --- a/server/src/services/Accounts/constants.ts +++ b/server/src/services/Accounts/constants.ts @@ -14,3 +14,62 @@ export const ERRORS = { 'close_account_and_to_account_not_same_type', ACCOUNTS_NOT_FOUND: 'accounts_not_found', }; + +// Default views columns. +export const DEFAULT_VIEW_COLUMNS = [ + { key: 'name', label: 'Account name' }, + { key: 'code', label: 'Account code' }, + { key: 'account_type_label', label: 'Account type' }, + { key: 'account_normal', label: 'Account normal' }, + { key: 'amount', label: 'Balance' }, + { key: 'currencyCode', label: 'Currency' }, +]; + +// Accounts default views. +export const DEFAULT_VIEWS = [ + { + name: 'Assets', + slug: 'assets', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'root_type', comparator: 'equals', value: 'asset' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Liabilities', + slug: 'liabilities', + rolesLogicExpression: '1', + roles: [ + { fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'liability' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Equity', + slug: 'equity', + rolesLogicExpression: '1', + roles: [ + { fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'equity' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Income', + slug: 'income', + rolesLogicExpression: '1', + roles: [ + { fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'income' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Expenses', + slug: 'expenses', + rolesLogicExpression: '1', + roles: [ + { fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'expense' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, +]; diff --git a/server/src/services/Contacts/Customers/constants.ts b/server/src/services/Contacts/Customers/constants.ts new file mode 100644 index 000000000..a40f96a00 --- /dev/null +++ b/server/src/services/Contacts/Customers/constants.ts @@ -0,0 +1,22 @@ +export const DEFAULT_VIEW_COLUMNS = []; + +export const DEFAULT_VIEWS = [ + { + name: 'Overdue', + slug: 'overdue', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'overdue' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Unpaid', + slug: 'unpaid', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'unpaid' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, +]; diff --git a/server/src/services/Contacts/Vendors/constants.ts b/server/src/services/Contacts/Vendors/constants.ts new file mode 100644 index 000000000..a40f96a00 --- /dev/null +++ b/server/src/services/Contacts/Vendors/constants.ts @@ -0,0 +1,22 @@ +export const DEFAULT_VIEW_COLUMNS = []; + +export const DEFAULT_VIEWS = [ + { + name: 'Overdue', + slug: 'overdue', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'overdue' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Unpaid', + slug: 'unpaid', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'unpaid' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, +]; diff --git a/server/src/services/Contacts/VendorsService.ts b/server/src/services/Contacts/VendorsService.ts index 6104eb15d..c2c1a037e 100644 --- a/server/src/services/Contacts/VendorsService.ts +++ b/server/src/services/Contacts/VendorsService.ts @@ -286,11 +286,12 @@ export default class VendorsService { Vendor, filter ); - // Vendors list. const { results, pagination } = await Vendor.query() .onBuild((builder) => { dynamicList.buildQuery()(builder); + + // Switches between active/inactive modes. builder.modify('inactiveMode', filter.inactiveMode); }) .pagination(filter.page - 1, filter.pageSize); diff --git a/server/src/services/Contacts/constants.ts b/server/src/services/Contacts/constants.ts new file mode 100644 index 000000000..a40f96a00 --- /dev/null +++ b/server/src/services/Contacts/constants.ts @@ -0,0 +1,22 @@ +export const DEFAULT_VIEW_COLUMNS = []; + +export const DEFAULT_VIEWS = [ + { + name: 'Overdue', + slug: 'overdue', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'overdue' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Unpaid', + slug: 'unpaid', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'unpaid' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, +]; diff --git a/server/src/services/DynamicListing/DynamicListCustomView.ts b/server/src/services/DynamicListing/DynamicListCustomView.ts index ecaff2177..7e11f54dc 100644 --- a/server/src/services/DynamicListing/DynamicListCustomView.ts +++ b/server/src/services/DynamicListing/DynamicListCustomView.ts @@ -3,8 +3,8 @@ import DynamicListAbstruct from './DynamicListAbstruct'; import DynamicFilterViews from 'lib/DynamicFilter/DynamicFilterViews'; import { ServiceError } from 'exceptions'; import HasTenancyService from 'services/Tenancy/TenancyService'; -import {ERRORS } from './constants'; -import { IModel }from 'interfaces'; +import { ERRORS } from './constants'; +import { IModel } from 'interfaces'; @Service() export default class DynamicListCustomView extends DynamicListAbstruct { @@ -19,16 +19,18 @@ export default class DynamicListCustomView extends DynamicListAbstruct { */ private getCustomViewOrThrowError = async ( tenantId: number, - viewId: number, + viewSlug: string, model: IModel ) => { - const { viewRepository } = this.tenancy.repositories(tenantId); - const view = await viewRepository.findOneById(viewId, 'roles'); + const { View } = this.tenancy.models(tenantId); - if (!view || view.resourceModel !== model.name) { + // Finds the default view by the given view slug. + const defaultView = model.getDefaultViewBySlug(viewSlug); + + if (!defaultView) { throw new ServiceError(ERRORS.VIEW_NOT_FOUND); } - return view; + return defaultView; }; /** @@ -38,14 +40,17 @@ export default class DynamicListCustomView extends DynamicListAbstruct { * @returns */ public dynamicListCustomView = async ( - tenantId: number, - model, - customViewId: number + dynamicFilter: any, + customViewSlug: string, + tenantId: number ) => { + const model = dynamicFilter.getModel(); + + // Retrieve the custom view or throw not found. const view = await this.getCustomViewOrThrowError( tenantId, - customViewId, - model + customViewSlug, + model, ); return new DynamicFilterViews(view); }; diff --git a/server/src/services/DynamicListing/DynamicListService.ts b/server/src/services/DynamicListing/DynamicListService.ts index f6eeb0233..40e4f4b35 100644 --- a/server/src/services/DynamicListing/DynamicListService.ts +++ b/server/src/services/DynamicListing/DynamicListService.ts @@ -30,16 +30,18 @@ export default class DynamicListService implements IDynamicListService { /** * Parses filter DTO. - * @param {IMode} model - - * @param {} filterDTO - + * @param {IMode} model - + * @param {} filterDTO - */ private parseFilterObject = (model, filterDTO) => { return { // Merges the default properties with filter object. - ...model.defaultSort ? { - sortOrder: model.defaultSort.sortOrder, - columnSortBy: model.defaultSort.sortOrder, - } : {}, + ...(model.defaultSort + ? { + sortOrder: model.defaultSort.sortOrder, + columnSortBy: model.defaultSort.sortOrder, + } + : {}), ...filterDTO, }; }; @@ -61,17 +63,21 @@ export default class DynamicListService implements IDynamicListService { const parsedFilter = this.parseFilterObject(model, filter); // Custom view filter roles. - // if (filter.customViewId) { - // const dynamicListCustomView = this.dynamicListView.dynamicListCustomView(); - - // dynamicFilter.setFilter(dynamicListCustomView); - // } + if (filter.viewSlug) { + const dynamicListCustomView = + await this.dynamicListView.dynamicListCustomView( + dynamicFilter, + filter.viewSlug, + tenantId + ); + dynamicFilter.setFilter(dynamicListCustomView); + } // Sort by the given column. if (parsedFilter.columnSortBy) { const dynmaicListSortBy = this.dynamicListSortBy.dynamicSortBy( model, parsedFilter.columnSortBy, - parsedFilter.sortOrder, + parsedFilter.sortOrder ); dynamicFilter.setFilter(dynmaicListSortBy); } diff --git a/server/src/services/Expenses/constants.ts b/server/src/services/Expenses/constants.ts new file mode 100644 index 000000000..01cc0789a --- /dev/null +++ b/server/src/services/Expenses/constants.ts @@ -0,0 +1,26 @@ +export const DEFAULT_VIEW_COLUMNS = []; +export const DEFAULT_VIEWS = [ + { + name: 'Draft', + slug: 'draft', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'draft' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Published', + slug: 'published', + rolesLogicExpression: '1', + roles: [ + { + index: 1, + fieldKey: 'status', + comparator: 'equals', + value: 'published', + }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, +]; diff --git a/server/src/services/Items/constants.ts b/server/src/services/Items/constants.ts index 5bae41ebd..bcb158229 100644 --- a/server/src/services/Items/constants.ts +++ b/server/src/services/Items/constants.ts @@ -1,3 +1,4 @@ + export const ERRORS = { NOT_FOUND: 'NOT_FOUND', ITEMS_NOT_FOUND: 'ITEMS_NOT_FOUND', @@ -21,3 +22,38 @@ export const ERRORS = { TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS: 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS', INVENTORY_ACCOUNT_CANNOT_MODIFIED: 'INVENTORY_ACCOUNT_CANNOT_MODIFIED' }; + + +export const DEFAULT_VIEW_COLUMNS = [ + +]; + +export const DEFAULT_VIEWS = [ + { + name: 'Services', + slug: 'services', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'type', comparator: 'equals', value: 'service' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Inventory', + slug: 'inventory', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'type', comparator: 'equals', value: 'inventory' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Non Inventory', + slug: 'non-inventory', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'type', comparator: 'equals', value: 'non-inventory' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, +] \ No newline at end of file diff --git a/server/src/services/Purchases/constants.ts b/server/src/services/Purchases/constants.ts index be1874a86..f3a6f5dc4 100644 --- a/server/src/services/Purchases/constants.ts +++ b/server/src/services/Purchases/constants.ts @@ -11,7 +11,65 @@ export const ERRORS = { BILL_HAS_ASSOCIATED_PAYMENT_ENTRIES: 'BILL_HAS_ASSOCIATED_PAYMENT_ENTRIES', VENDOR_HAS_BILLS: 'VENDOR_HAS_BILLS', BILL_HAS_ASSOCIATED_LANDED_COSTS: 'BILL_HAS_ASSOCIATED_LANDED_COSTS', - BILL_ENTRIES_ALLOCATED_COST_COULD_DELETED: 'BILL_ENTRIES_ALLOCATED_COST_COULD_DELETED', - LOCATED_COST_ENTRIES_SHOULD_BIGGE_THAN_NEW_ENTRIES: 'LOCATED_COST_ENTRIES_SHOULD_BIGGE_THAN_NEW_ENTRIES', - LANDED_COST_ENTRIES_SHOULD_BE_INVENTORY_ITEMS: 'LANDED_COST_ENTRIES_SHOULD_BE_INVENTORY_ITEMS' + BILL_ENTRIES_ALLOCATED_COST_COULD_DELETED: + 'BILL_ENTRIES_ALLOCATED_COST_COULD_DELETED', + LOCATED_COST_ENTRIES_SHOULD_BIGGE_THAN_NEW_ENTRIES: + 'LOCATED_COST_ENTRIES_SHOULD_BIGGE_THAN_NEW_ENTRIES', + LANDED_COST_ENTRIES_SHOULD_BE_INVENTORY_ITEMS: + 'LANDED_COST_ENTRIES_SHOULD_BE_INVENTORY_ITEMS', }; + +export const DEFAULT_VIEW_COLUMNS = []; + +export const DEFAULT_VIEWS = [ + { + name: 'Draft', + slug: 'draft', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'draft' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Opended', + slug: 'opened', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'opened' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Unpaid', + slug: 'unpaid', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'unpaid' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Overdue', + slug: 'overdue', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'overdue' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Partially Paid', + slug: 'partially-paid', + rolesLogicExpression: '1', + roles: [ + { + index: 1, + fieldKey: 'status', + comparator: 'equals', + value: 'partially-paid', + }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, +]; diff --git a/server/src/services/Sales/Estimates/constants.ts b/server/src/services/Sales/Estimates/constants.ts new file mode 100644 index 000000000..3e4b4287d --- /dev/null +++ b/server/src/services/Sales/Estimates/constants.ts @@ -0,0 +1,96 @@ +export const DEFAULT_VIEW_COLUMNS = []; +export const DEFAULT_VIEWS = [ + { + name: 'Draft', + slug: 'draft', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'draft' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Delivered', + slug: 'delivered', + rolesLogicExpression: '1', + roles: [ + { + index: 1, + fieldKey: 'status', + comparator: 'equals', + value: 'delivered', + }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Approved', + slug: 'approved', + rolesLogicExpression: '1', + roles: [ + { + index: 1, + fieldKey: 'status', + comparator: 'equals', + value: 'approved', + }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Rejected', + slug: 'rejected', + rolesLogicExpression: '1', + roles: [ + { + index: 1, + fieldKey: 'status', + comparator: 'equals', + value: 'rejected', + }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Invoiced', + slug: 'invoiced', + rolesLogicExpression: '1', + roles: [ + { + index: 1, + fieldKey: 'status', + comparator: 'equals', + value: 'invoiced', + }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Expired', + slug: 'expired', + rolesLogicExpression: '1', + roles: [ + { + index: 1, + fieldKey: 'status', + comparator: 'equals', + value: 'expired', + }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Closed', + slug: 'closed', + rolesLogicExpression: '1', + roles: [ + { + index: 1, + fieldKey: 'status', + comparator: 'equals', + value: 'closed', + }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, +]; diff --git a/server/src/services/Sales/Receipts/constants.ts b/server/src/services/Sales/Receipts/constants.ts new file mode 100644 index 000000000..9707bb1ec --- /dev/null +++ b/server/src/services/Sales/Receipts/constants.ts @@ -0,0 +1,21 @@ +export const DEFAULT_VIEW_COLUMNS = []; +export const DEFAULT_VIEWS = [ + { + name: 'Draft', + slug: 'draft', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'draft' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Closed', + slug: 'closed', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'closed' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, +]; diff --git a/server/src/services/Sales/constants.ts b/server/src/services/Sales/constants.ts index e1e596dfb..2666542e2 100644 --- a/server/src/services/Sales/constants.ts +++ b/server/src/services/Sales/constants.ts @@ -10,5 +10,63 @@ export const ERRORS = { INVOICE_HAS_ASSOCIATED_PAYMENT_ENTRIES: 'INVOICE_HAS_ASSOCIATED_PAYMENT_ENTRIES', SALE_INVOICE_NO_IS_REQUIRED: 'SALE_INVOICE_NO_IS_REQUIRED', - CUSTOMER_HAS_SALES_INVOICES: 'CUSTOMER_HAS_SALES_INVOICES' + CUSTOMER_HAS_SALES_INVOICES: 'CUSTOMER_HAS_SALES_INVOICES', }; +export const DEFAULT_VIEW_COLUMNS = []; +export const DEFAULT_VIEWS = [ + { + name: 'Draft', + slug: 'draft', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'draft' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Delivered', + slug: 'delivered', + rolesLogicExpression: '1', + roles: [ + { + index: 1, + fieldKey: 'status', + comparator: 'equals', + value: 'delivered', + }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Unpaid', + slug: 'unpaid', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'unpaid' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Partially paid', + slug: 'partially-paid', + rolesLogicExpression: '1', + roles: [ + { + index: 1, + fieldKey: 'status', + comparator: 'equals', + value: 'partially-paid', + }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, + { + name: 'Paid', + slug: 'paid', + rolesLogicExpression: '1', + roles: [ + { index: 1, fieldKey: 'status', comparator: 'equals', value: 'paid' }, + ], + columns: DEFAULT_VIEW_COLUMNS, + }, +];