From 3546b6b7aec6111b0355e9b035c5dd7c73ab27dc Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Sat, 31 Jul 2021 17:07:37 +0200 Subject: [PATCH] WIP: dynamic list filtering. --- .../Inventory/InventoryAdjustments.ts | 2 + server/src/interfaces/Model.ts | 15 +++- server/src/lib/DynamicFilter/DynamicFilter.ts | 9 ++- .../DynamicFilter/DynamicFilterAbstructor.ts | 33 +++++---- .../DynamicFilter/DynamicFilterFilterRoles.ts | 14 ++++ .../DynamicFilterRoleAbstructor.ts | 63 ++++++++++++++-- .../lib/DynamicFilter/DynamicFilterSortBy.ts | 68 +++++++++++------- server/src/lib/DynamicFilter/constants.ts | 1 + server/src/models/Account.Settings.ts | 53 +++++++++++--- server/src/models/Account.js | 25 ++++++- server/src/models/Bill.Settings.ts | 57 +++++++++------ server/src/models/Bill.js | 61 ++++++++-------- server/src/models/BillPayment.Settings.ts | 22 +++--- server/src/models/BillPayment.js | 4 +- server/src/models/Expense.Settings.ts | 33 +++------ server/src/models/Expense.js | 19 +++-- .../models/InventoryAdjustment.Settings.ts | 15 ++-- server/src/models/InventoryAdjustment.js | 5 +- server/src/models/Item.Settings.ts | 33 +++++++-- server/src/models/Item.js | 2 +- server/src/models/ItemCategory.Settings.ts | 15 +--- server/src/models/ItemCategory.js | 34 ++++++--- server/src/models/ManualJournal.Settings.ts | 4 +- server/src/models/ModelSetting.ts | 6 +- server/src/models/PaymentReceive.Settings.ts | 14 ++++ server/src/models/SaleEstimate.Settings.ts | 46 ++++++------ server/src/models/SaleEstimate.js | 27 ++++++- server/src/models/SaleInvoice.Settings.ts | 72 +++++++++++-------- server/src/models/SaleInvoice.js | 62 ++++++++-------- server/src/models/SaleReceipt.Settings.ts | 42 ++++++----- server/src/models/SaleReceipt.js | 19 ++++- .../DynamicListing/DynamicListService.ts | 2 + .../Inventory/InventoryAdjustmentService.ts | 5 +- .../ItemCategories/ItemCategoriesService.ts | 1 + 34 files changed, 569 insertions(+), 314 deletions(-) diff --git a/server/src/api/controllers/Inventory/InventoryAdjustments.ts b/server/src/api/controllers/Inventory/InventoryAdjustments.ts index ff4276e36..9e9481289 100644 --- a/server/src/api/controllers/Inventory/InventoryAdjustments.ts +++ b/server/src/api/controllers/Inventory/InventoryAdjustments.ts @@ -62,6 +62,8 @@ export default class InventoryAdjustmentsController extends BaseController { query('page').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(), + + query('stringified_filter_roles').optional().isJSON(), ]; } diff --git a/server/src/interfaces/Model.ts b/server/src/interfaces/Model.ts index 1589a07b6..67b90e872 100644 --- a/server/src/interfaces/Model.ts +++ b/server/src/interfaces/Model.ts @@ -59,12 +59,21 @@ export interface IModelMetaEnumerationField { options: IModelMetaEnumerationOption[]; } -export interface IModelMetaRelationField { +export interface IModelMetaRelationFieldCommon { fieldType: 'relation'; - relationToModel: IModel; - relationToField: string; } +export interface IModelMetaRelationEnumerationField { + relationType: 'enumeration'; + relationKey: string; + relationEntityLabel: string; + relationEntityKey: string; +} + +export type IModelMetaRelationField = IModelMetaRelationFieldCommon & ( + IModelMetaRelationEnumerationField +); + export interface IModelMeta { defaultFilterField: string; defaultSort: IModelMetaDefaultSort; diff --git a/server/src/lib/DynamicFilter/DynamicFilter.ts b/server/src/lib/DynamicFilter/DynamicFilter.ts index 669f7952a..92dca7203 100644 --- a/server/src/lib/DynamicFilter/DynamicFilter.ts +++ b/server/src/lib/DynamicFilter/DynamicFilter.ts @@ -25,6 +25,9 @@ export default class DynamicFilter extends DynamicFilterAbstructor{ */ public setFilter = (dynamicFilter: IDynamicFilter) => { dynamicFilter.setModel(this.model); + + dynamicFilter.onInitialize(); + this.dynamicFilters.push(dynamicFilter); } @@ -66,11 +69,7 @@ export default class DynamicFilter extends DynamicFilterAbstructor{ buildersCallbacks.forEach((builderCallback) => { builderCallback(builder); }); - - this.buildFilterRolesJoins( - this.model, - uniqBy(tableColumns, 'columnKey') - )(builder); + this.buildFilterRolesJoins(builder); }; } diff --git a/server/src/lib/DynamicFilter/DynamicFilterAbstructor.ts b/server/src/lib/DynamicFilter/DynamicFilterAbstructor.ts index 44c50560f..17f2afad7 100644 --- a/server/src/lib/DynamicFilter/DynamicFilterAbstructor.ts +++ b/server/src/lib/DynamicFilter/DynamicFilterAbstructor.ts @@ -1,4 +1,5 @@ import { IModel, IFilterRole } from 'interfaces'; +import { FIELD_TYPE } from './constants'; export default class DynamicFilterAbstructor { /** @@ -16,22 +17,24 @@ export default class DynamicFilterAbstructor { * @param {String} tableName - Table name. * @param {Array} roles - Roles. */ - protected buildFilterRolesJoins = (model: IModel, roles: IFilterRole[]) => { - return (builder) => { - roles.forEach((role) => { - const field = model.getField(role.fieldKey); + protected buildFilterRolesJoins = (builder) => { + this.dynamicFilters.forEach((dynamicFilter) => { + const relationsFields = dynamicFilter.relationFields; - if (field.relation) { - const joinTable = this.getTableFromRelationColumn(field.relation); + this.buildFieldsJoinQueries(builder, relationsFields); + }); + }; - builder.join( - joinTable, - `${model.tableName}.${field.column}`, - '=', - field.relation - ); - } - }); - }; + private buildFieldsJoinQueries = (builder, fieldsRelations: string[]) => { + fieldsRelations.forEach((fieldRelation) => { + const relation = this.model.relationMappings[fieldRelation]; + + if (relation) { + const splitToRelation = relation.join.to.split('.'); + const relationTable = splitToRelation[0] || ''; + + builder.join(relationTable, relation.join.from, '=', relation.join.to); + } + }); }; } diff --git a/server/src/lib/DynamicFilter/DynamicFilterFilterRoles.ts b/server/src/lib/DynamicFilter/DynamicFilterFilterRoles.ts index a37cdb0fe..fb8374232 100644 --- a/server/src/lib/DynamicFilter/DynamicFilterFilterRoles.ts +++ b/server/src/lib/DynamicFilter/DynamicFilterFilterRoles.ts @@ -13,9 +13,14 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor { super(); this.filterRoles = filterRoles; + this.setResponseMeta(); } + public onInitialize() { + this.setFilterRolesRelations(); + } + /** * Builds filter roles logic expression. * @return {string} @@ -53,4 +58,13 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor { filterRoles: this.filterRoles, }; } + + /** + * Sets filter roles relations if field was relation type. + */ + private setFilterRolesRelations() { + this.filterRoles.forEach((relationRole) => { + this.setRelationIfRelationField(relationRole.fieldKey); + }); + } } diff --git a/server/src/lib/DynamicFilter/DynamicFilterRoleAbstructor.ts b/server/src/lib/DynamicFilter/DynamicFilterRoleAbstructor.ts index 25dc30c8e..25ee0c340 100644 --- a/server/src/lib/DynamicFilter/DynamicFilterRoleAbstructor.ts +++ b/server/src/lib/DynamicFilter/DynamicFilterRoleAbstructor.ts @@ -12,6 +12,7 @@ export default abstract class DynamicFilterAbstructor protected tableName: string; protected model: IModel; protected responseMeta: { [key: string]: any } = {}; + public relationFields = []; /** * Sets model the dynamic filter service. @@ -78,22 +79,50 @@ export default abstract class DynamicFilterAbstructor }; }; + /** + * Retrieve relation column of comparator fieldŲ² + */ + private getFieldComparatorRelationColumn(field) { + const relation = this.model.relationMappings[field.relationKey]; + + if (relation) { + const relationModel = relation.modelClass; + const relationColumn = + field.relationEntityKey === 'id' + ? 'id' + : relationModel.getField(field.relationEntityKey, 'column'); + + return `${relationModel.tableName}.${relationColumn}`; + } + } + + /** + * Retrieve the comparator field column. + * @param {IModel} model - + * @param {} - + */ + private getFieldComparatorColumn = (field) => { + return field.fieldType === FIELD_TYPE.RELATION + ? this.getFieldComparatorRelationColumn(field) + : `${this.tableName}.${field.column}`; + }; + /** * Builds roles queries. * @param {IModel} model - * @param {Object} role - */ protected buildRoleQuery = (model: IModel, role: IFilterRole) => { - const fieldRelation = model.getField(role.fieldKey); - const comparatorColumn = `${model.tableName}.${fieldRelation.column}`; + const field = model.getField(role.fieldKey); + const comparatorColumn = this.getFieldComparatorColumn(field); // Field relation custom query. - if (typeof fieldRelation.customQuery !== 'undefined') { + if (typeof field.filterCustomQuery !== 'undefined') { return (builder) => { - fieldRelation.customQuery(builder, role); + field.filterCustomQuery(builder, role); }; } - switch (fieldRelation.fieldType) { + switch (field.fieldType) { case FIELD_TYPE.BOOLEAN: case FIELD_TYPE.ENUMERATION: return this.booleanRoleQueryBuilder(role, comparatorColumn); @@ -265,7 +294,7 @@ export default abstract class DynamicFilterAbstructor builder.where(comparatorColumn, '>=', startDate.format(dateFormat)); builder.where(comparatorColumn, '<=', endDate.format(dateFormat)); } - } + }; /** * Date query after/before comparator type. @@ -296,5 +325,25 @@ export default abstract class DynamicFilterAbstructor } const comparatorValue = targetDate.format(dateFormat); builder.where(comparatorColumn, comparator, comparatorValue); - } + }; + + /** + * Registers relation field if the given field was relation type + * and not registered. + * @param {string} fieldKey - Field key. + */ + protected setRelationIfRelationField = (fieldKey: string): void => { + const field = this.model.getField(fieldKey); + const isAlreadyRegistered = this.relationFields.some( + (field) => field === fieldKey + ); + + if ( + !isAlreadyRegistered && + field && + field.fieldType === FIELD_TYPE.RELATION + ) { + this.relationFields.push(field.relationKey); + } + }; } diff --git a/server/src/lib/DynamicFilter/DynamicFilterSortBy.ts b/server/src/lib/DynamicFilter/DynamicFilterSortBy.ts index 2ed89faec..433211481 100644 --- a/server/src/lib/DynamicFilter/DynamicFilterSortBy.ts +++ b/server/src/lib/DynamicFilter/DynamicFilterSortBy.ts @@ -1,9 +1,5 @@ import DynamicFilterRoleAbstructor from 'lib/DynamicFilter/DynamicFilterRoleAbstructor'; -import { - getRoleFieldColumn, - validateFieldKeyExistance, - getTableFromRelationColumn, -} from 'lib/ViewRolesBuilder'; +import { FIELD_TYPE } from './constants'; interface ISortRole { fieldKey: string; @@ -28,17 +24,52 @@ export default class DynamicFilterSortBy extends DynamicFilterRoleAbstructor { this.setResponseMeta(); } + /** + * On initialize the dyanmic sort by. + */ + public onInitialize() { + this.setRelationIfRelationField(this.sortRole.fieldKey); + } + + /** + * Retrieve field comparator relatin column. + * @param field + * @returns {string} + */ + private getFieldComparatorRelationColumn = (field): string => { + const relation = this.model.relationMappings[field.relationKey]; + + if (relation) { + const relationModel = relation.modelClass; + const relationField = relationModel.getField(field.relationEntityLabel); + + return `${relationModel.tableName}.${relationField.column}`; + } + return ''; + }; + + /** + * Retrieve the comparator field column. + * @param {IModel} field + * @returns {string} + */ + private getFieldComparatorColumn = (field): string => { + return field.fieldType === FIELD_TYPE.RELATION + ? this.getFieldComparatorRelationColumn(field) + : `${this.tableName}.${field.column}`; + }; /** * Builds database query of sort by column on the given direction. */ - public buildQuery() { + public buildQuery = () => { const field = this.model.getField(this.sortRole.fieldKey); - const comparatorColumn = `${this.tableName}.${field.column}`; + const comparatorColumn = this.getFieldComparatorColumn(field); - if (typeof field.customSortQuery !== 'undefined') { + // Sort custom query. + if (typeof field.sortCustomQuery !== 'undefined') { return (builder) => { - field.customSortQuery(builder, this.sortRole); + field.sortCustomQuery(builder, this.sortRole); }; } @@ -47,24 +78,7 @@ export default class DynamicFilterSortBy extends DynamicFilterRoleAbstructor { builder.orderBy(`${comparatorColumn}`, this.sortRole.order); } }; - } - - private joinBuildQuery() { - const fieldColumn = getRoleFieldColumn(this.model, this.sortRole.fieldKey); - - return (builder) => { - if (fieldColumn.relation) { - const joinTable = getTableFromRelationColumn(fieldColumn.relation); - - builder.join( - joinTable, - `${this.model.tableName}.${fieldColumn.column}`, - '=', - fieldColumn.relation - ); - } - }; - } + }; /** * Sets response meta. diff --git a/server/src/lib/DynamicFilter/constants.ts b/server/src/lib/DynamicFilter/constants.ts index 52ec3986e..6dcea30c2 100644 --- a/server/src/lib/DynamicFilter/constants.ts +++ b/server/src/lib/DynamicFilter/constants.ts @@ -33,4 +33,5 @@ export const FIELD_TYPE = { BOOLEAN: 'boolean', RELATION: 'relation', DATE: 'date', + COMPUTED: 'computed' }; diff --git a/server/src/models/Account.Settings.ts b/server/src/models/Account.Settings.ts index ff1e85435..5494a7abb 100644 --- a/server/src/models/Account.Settings.ts +++ b/server/src/models/Account.Settings.ts @@ -1,4 +1,5 @@ import { IModelMeta } from 'interfaces'; +import { ACCOUNT_TYPES } from 'data/AccountTypes'; export default { defaultFilterField: 'name', @@ -10,25 +11,26 @@ export default { name: { name: 'Account name', column: 'name', - columnable: true, fieldType: 'text', }, description: { name: 'Description', column: 'description', - columnable: true, fieldType: 'text', }, + slug: { + name: 'Account slug', + column: 'slug', + fieldType: 'text', + columnable: false, + }, code: { name: 'Account code', column: 'code', - columnable: true, fieldType: 'text', }, root_type: { name: 'Root type', - column: 'root_type', - columnable: true, fieldType: 'enumeration', options: [ { key: 'asset', label: 'Asset' }, @@ -37,16 +39,37 @@ export default { { key: 'Income', label: 'Income' }, { key: 'expense', label: 'Expense' }, ], + filterCustomQuery: RootTypeFieldFilterQuery, + sortable: false, + }, + normal: { + name: 'Account normal', + fieldType: 'enumeration', + options: [ + { key: 'debit', label: 'Debit' }, + { key: 'credit', label: 'Credit' }, + ], + filterCustomQuery: NormalTypeFieldFilterQuery, + sortable: false, + }, + type: { + name: 'Type', + column: 'account_type', + fieldType: 'enumeration', + options: ACCOUNT_TYPES.map((accountType) => ({ + label: accountType.label, + key: accountType.key + })), }, active: { name: 'Active', column: 'active', fieldType: 'boolean', + filterable: false, }, - amount: { + balance: { name: 'Account balance', column: 'amount', - columnable: true, fieldType: 'number', }, currency: { @@ -60,4 +83,18 @@ export default { fieldType: 'date', }, }, -} as IModelMeta; +}; + +/** + * Filter query of root type field . + */ +function RootTypeFieldFilterQuery(query, role) { + query.modify('filterByRootType', role.value); +} + +/** + * Filter query of normal field . + */ +function NormalTypeFieldFilterQuery(query, role) { + query.modify('filterByAccountNormal', role.value); +} diff --git a/server/src/models/Account.js b/server/src/models/Account.js index e435542cc..29e5ad1f8 100644 --- a/server/src/models/Account.js +++ b/server/src/models/Account.js @@ -8,6 +8,7 @@ import DependencyGraph from 'lib/DependencyGraph'; import AccountTypesUtils from 'lib/AccountTypes'; import AccountSettings from './Account.Settings'; import ModelSettings from './ModelSetting'; +import { ACCOUNT_TYPES } from 'data/AccountTypes'; export default class Account extends mixin(TenantModel, [ModelSettings]) { /** @@ -98,7 +99,7 @@ export default class Account extends mixin(TenantModel, [ModelSettings]) { * Inactive/Active mode. */ inactiveMode(query, active = false) { - query.where('active', !active); + query.where('accounts.active', !active); }, filterAccounts(query, accountIds) { @@ -117,6 +118,28 @@ export default class Account extends mixin(TenantModel, [ModelSettings]) { sortColumnBuilder(query, columnKey, direction) { buildSortColumnQuery(Account.tableName, columnKey, direction)(query); }, + + /** + * Filter by root type. + */ + filterByRootType(query, rootType) { + const filterTypes = ACCOUNT_TYPES.filter( + (accountType) => accountType.rootType === rootType + ).map((accountType) => accountType.key); + + query.whereIn('account_type', filterTypes); + }, + + /** + * Filter by account normal + */ + filterByAccountNormal(query, accountNormal) { + const filterTypes = ACCOUNT_TYPES.filter( + (accountType) => accountType.normal === accountNormal, + ).map((accountType) => accountType.key); + + query.whereIn('account_type', filterTypes); + }, }; } diff --git a/server/src/models/Bill.Settings.ts b/server/src/models/Bill.Settings.ts index 6a01455f1..94d27fd58 100644 --- a/server/src/models/Bill.Settings.ts +++ b/server/src/models/Bill.Settings.ts @@ -8,35 +8,42 @@ export default { sortField: 'bill_date', }, fields: { - // vendor: { - // name: 'Vendor', - // column: 'vendor_id', - // }, - 'bill_number': { + vendor: { + name: 'Vendor', + column: 'vendor_id', + fieldType: 'relation', + + relationType: 'enumeration', + relationKey: 'vendor', + + relationEntityLabel: 'name', + relationEntityKey: 'id', + }, + bill_number: { name: 'Bill number', column: 'bill_number', columnable: true, fieldType: 'text', }, - 'bill_date': { + bill_date: { name: 'Bill date', column: 'bill_date', columnable: true, fieldType: 'date', }, - 'due_date': { + due_date: { name: 'Due date', column: 'due_date', columnable: true, fieldType: 'date', }, - 'reference_no': { + reference_no: { name: 'Reference No.', column: 'reference_no', columnable: true, fieldType: 'text', }, - 'status': { + status: { name: 'Status', fieldType: 'enumeration', columnable: true, @@ -48,32 +55,42 @@ export default { { name: 'Opened', key: 'opened' }, { name: 'Draft', key: 'draft' }, ], - // filterQuery: Bill.statusFieldFilterQuery, - // sortQuery: Bill.statusFieldSortQuery, + filterCustomQuery: StatusFieldFilterQuery, + sortCustomQuery: StatusFieldSortQuery, }, - 'amount': { + amount: { name: 'Amount', column: 'amount', - columnable: true, fieldType: 'number', }, - 'payment_amount': { + payment_amount: { name: 'Payment amount', column: 'payment_amount', - columnable: true, fieldType: 'number', }, - 'note': { + note: { name: 'Note', column: 'note', - columnable: true, fieldType: 'text', }, - 'created_at': { + created_at: { name: 'Created at', column: 'created_at', - columnable: true, fieldType: 'date', }, }, -} as IModelMeta; +}; + +/** + * Status field filter custom query. + */ +function StatusFieldFilterQuery(query, role) { + query.modify('statusFilter', role.value); +} + +/** + * Status field sort custom query. + */ +function StatusFieldSortQuery(query, role) { + query.modify('sortByStatus', role.order); +} diff --git a/server/src/models/Bill.js b/server/src/models/Bill.js index 51ea5afb8..61a354e53 100644 --- a/server/src/models/Bill.js +++ b/server/src/models/Bill.js @@ -71,15 +71,42 @@ export default class Bill extends mixin(TenantModel, [ModelSetting]) { * Filters the bills from the given date. */ fromDate(query, fromDate) { - query.where('bill_date', '<=', fromDate) + query.where('bill_date', '<=', fromDate); }, /** * Sort the bills by full-payment bills. */ - sortByStatus(query, order) { + sortByStatus(query, order) { query.orderByRaw(`PAYMENT_AMOUNT = AMOUNT ${order}`); }, + + /** + * Status filter. + */ + statusFilter(query, filterType) { + switch (filterType) { + case 'draft': + query.modify('draft'); + break; + case 'delivered': + query.modify('delivered'); + break; + case 'unpaid': + query.modify('unpaid'); + break; + case 'overdue': + default: + query.modify('overdue'); + break; + case 'partially-paid': + query.modify('partiallyPaid'); + break; + case 'paid': + query.modify('paid'); + break; + } + }, }; } @@ -103,7 +130,7 @@ export default class Bill extends mixin(TenantModel, [ModelSetting]) { 'remainingDays', 'overdueDays', 'isOverdue', - 'unallocatedCostAmount' + 'unallocatedCostAmount', ]; } @@ -276,32 +303,4 @@ export default class Bill extends mixin(TenantModel, [ModelSetting]) { .where('id', billId) [changeMethod]('payment_amount', Math.abs(amount)); } - - - static statusFieldFilterQuery(query, role) { - switch (role.value) { - case 'draft': - query.modify('draft'); - break; - case 'opened': - query.modify('opened'); - break; - case 'unpaid': - query.modify('unpaid'); - break; - case 'overdue': - query.modify('overdue'); - break; - case 'partially-paid': - query.modify('partiallyPaid'); - break; - case 'paid': - query.modify('paid'); - break; - } - }; - - static statusFieldSortQuery(query, role) { - return query.modify('sortByStatus', role.order); - }; } diff --git a/server/src/models/BillPayment.Settings.ts b/server/src/models/BillPayment.Settings.ts index f87c2e6b9..261328036 100644 --- a/server/src/models/BillPayment.Settings.ts +++ b/server/src/models/BillPayment.Settings.ts @@ -1,4 +1,3 @@ -import { IModelMeta } from 'interfaces'; export default { defaultFilterField: 'vendor', @@ -10,51 +9,58 @@ export default { 'vendor': { name: 'Vendor name', column: 'vendor_id', + fieldType: 'relation', + + relationType: 'enumeration', + relationKey: 'vendor', + + relationEntityLabel: 'display_name', + relationEntityKey: 'id', }, 'amount': { name: 'Amount', column: 'amount', - columnable: true, fieldType: 'number', }, 'due_amount': { name: 'Due amount', column: 'due_amount', - columnable: true, fieldType: 'number', }, 'payment_account': { name: 'Payment account', column: 'payment_account_id', + + fieldType: 'relation', + fieldRelation: 'paymentAccount', + + fieldRelationType: 'enumeration', + relationLabelField: 'name', + relationKeyField: 'slug', }, 'payment_number': { name: 'Payment number', column: 'payment_number', - columnable: true, fieldType: 'number', }, 'payment_date': { name: 'Payment date', column: 'payment_date', - columnable: true, fieldType: 'date', }, 'reference_no': { name: 'Reference No.', column: 'reference', - columnable: true, fieldType: 'text', }, 'description': { name: 'Description', column: 'description', - columnable: true, fieldType: 'text', }, 'created_at': { name: 'Created at', column: 'created_at', - columnable: true, fieldType: 'date', }, }, diff --git a/server/src/models/BillPayment.js b/server/src/models/BillPayment.js index 323b8219c..dc150741d 100644 --- a/server/src/models/BillPayment.js +++ b/server/src/models/BillPayment.js @@ -31,7 +31,7 @@ export default class BillPayment extends mixin(TenantModel, [ModelSetting]) { static get relationMappings() { const BillPaymentEntry = require("models/BillPaymentEntry"); const AccountTransaction = require("models/AccountTransaction"); - const Contact = require("models/Contact"); + const Vendor = require("models/Vendor"); const Account = require("models/Account"); return { @@ -46,7 +46,7 @@ export default class BillPayment extends mixin(TenantModel, [ModelSetting]) { vendor: { relation: Model.BelongsToOneRelation, - modelClass: Contact.default, + modelClass: Vendor.default, join: { from: "bills_payments.vendorId", to: "contacts.id", diff --git a/server/src/models/Expense.Settings.ts b/server/src/models/Expense.Settings.ts index c97b9ef55..091c9b6cd 100644 --- a/server/src/models/Expense.Settings.ts +++ b/server/src/models/Expense.Settings.ts @@ -16,23 +16,19 @@ export default { 'payment_account': { name: 'Payment account', column: 'payment_account_id', - fieldType: 'relation', - fieldRelation: 'paymentAccount', - fieldRelationType: 'enumeration', - relationLabelField: 'name', - relationKeyField: 'slug', + relationType: 'enumeration', + relationKey: 'paymentAccount', + + relationEntityLabel: 'name', + relationEntityKey: 'slug', }, 'amount': { name: 'Amount', column: 'total_amount', fieldType: 'number', }, - // currency_code: { - // name: 'Currency', - // column: 'currency_code', - // }, 'reference_no': { name: 'Reference No.', column: 'reference_no', @@ -55,8 +51,8 @@ export default { { key: 'draft', name: 'Draft' }, { key: 'published', name: 'Published' }, ], - filterQuery: statusFieldFilterQuery, - sortQuery: statusFieldSortQuery, + filterCustomQuery: StatusFieldFilterQuery, + sortCustomQuery: StatusFieldSortQuery, }, 'created_at': { name: 'Created at', @@ -66,17 +62,10 @@ export default { }, }; -function statusFieldFilterQuery(query, role) { - switch (role.value) { - case 'draft': - query.modify('filterByDraft'); - break; - case 'published': - query.modify('filterByPublished'); - break; - } +function StatusFieldFilterQuery(query, role) { + query.modify('filterByStatus', role.value); } -function statusFieldSortQuery(query, role) { - return query.modify('sortByStatus', role.order); +function StatusFieldSortQuery(query, role) { + query.modify('sortByStatus', role.order); } diff --git a/server/src/models/Expense.js b/server/src/models/Expense.js index 4e96c9636..fc8afbcd5 100644 --- a/server/src/models/Expense.js +++ b/server/src/models/Expense.js @@ -27,13 +27,8 @@ export default class Expense extends mixin(TenantModel, [ModelSetting]) { } /** - * Allows to mark model as resourceable to viewable and filterable. + * Virtual attributes. */ - static get resourceable() { - return true; - } - - static get virtualAttributes() { return ['isPublished', 'unallocatedCostAmount']; } @@ -92,6 +87,18 @@ export default class Expense extends mixin(TenantModel, [ModelSetting]) { filterByPublished(query) { query.whereNot('published_at', null); }, + + filterByStatus(query, status) { + switch (status) { + case 'draft': + query.modify('filterByDraft'); + break; + case 'published': + default: + query.modify('filterByPublished'); + break; + } + }, }; } diff --git a/server/src/models/InventoryAdjustment.Settings.ts b/server/src/models/InventoryAdjustment.Settings.ts index a005aa6d7..0ca469b2f 100644 --- a/server/src/models/InventoryAdjustment.Settings.ts +++ b/server/src/models/InventoryAdjustment.Settings.ts @@ -9,7 +9,6 @@ export default { name: 'Date', column: 'date', fieldType: 'date', - columnable: true, }, 'type': { name: 'Adjustment type', @@ -19,42 +18,42 @@ export default { { key: 'increment', name: 'Increment' }, { key: 'decrement', name: 'Decrement' }, ], - columnable: true, }, 'adjustment_account': { name: 'Adjustment account', column: 'adjustment_account_id', - columnable: true, + fieldType: 'relation', + + relationType: 'enumeration', + relationKey: 'adjustmentAccount', + + relationEntityLabel: 'name', + relationEntityKey: 'slug', }, 'reason': { name: 'Reason', column: 'reason', fieldType: 'text', - columnable: true, }, 'reference_no': { name: 'Reference No.', column: 'reference_no', fieldType: 'text', - columnable: true, }, 'description': { name: 'Description', column: 'description', fieldType: 'text', - columnable: true, }, 'published_at': { name: 'Published at', column: 'published_at', fieldType: 'date', - columnable: true, }, 'created_at': { name: 'Created at', column: 'created_at', fieldType: 'date', - columnable: true, }, }, }; diff --git a/server/src/models/InventoryAdjustment.js b/server/src/models/InventoryAdjustment.js index b43220687..d489a4dfc 100644 --- a/server/src/models/InventoryAdjustment.js +++ b/server/src/models/InventoryAdjustment.js @@ -1,8 +1,9 @@ -import { Model } from 'objection'; +import { Model, mixin } from 'objection'; import TenantModel from 'models/TenantModel'; import InventoryAdjustmentSettings from './InventoryAdjustment.Settings'; +import ModelSetting from './ModelSetting'; -export default class InventoryAdjustment extends TenantModel { +export default class InventoryAdjustment extends mixin(TenantModel, [ModelSetting]) { /** * Table name */ diff --git a/server/src/models/Item.Settings.ts b/server/src/models/Item.Settings.ts index 590e775ea..403b889a9 100644 --- a/server/src/models/Item.Settings.ts +++ b/server/src/models/Item.Settings.ts @@ -8,7 +8,6 @@ export default { 'type': { name: 'Item type', column: 'type', - columnable: true, fieldType: 'enumeration', options: [ { key: 'inventory', label: 'Inventory', }, @@ -19,13 +18,11 @@ export default { 'name': { name: 'Name', column: 'name', - columnable: true, fieldType: 'text', }, 'code': { name: 'Code', column: 'code', - columnable: true, fieldType: 'text', }, 'sellable': { @@ -51,15 +48,34 @@ export default { 'cost_account': { name: 'Cost account', column: 'cost_account_id', - columnable: true, + fieldType: 'relation', + + relationType: 'enumeration', + relationKey: 'costAccount', + + relationEntityLabel: 'name', + relationEntityKey: 'slug', }, 'sell_account': { name: 'Sell account', column: 'sell_account_id', + fieldType: 'relation', + + relationType: 'enumeration', + relationKey: 'sellAccount', + + relationEntityLabel: 'name', + relationEntityKey: 'slug', }, 'inventory_account': { name: 'Inventory account', column: 'inventory_account_id', + + relationType: 'enumeration', + relationKey: 'inventoryAccount', + + relationEntityLabel: 'name', + relationEntityKey: 'slug', }, 'sell_description': { name: 'Sell description', @@ -80,17 +96,22 @@ export default { name: 'Note', column: 'note', fieldType: 'text', - columnable: true, }, 'category': { name: 'Category', column: 'category_id', - columnable: true, + + relationType: 'enumeration', + relationKey: 'category', + + relationEntityLabel: 'name', + relationEntityKey: 'id', }, 'active': { name: 'Active', column: 'active', fieldType: 'boolean', + filterable: false, }, 'created_at': { name: 'Created at', diff --git a/server/src/models/Item.js b/server/src/models/Item.js index e249302f4..9ab191bf1 100644 --- a/server/src/models/Item.js +++ b/server/src/models/Item.js @@ -42,7 +42,7 @@ export default class Item extends mixin(TenantModel, [ModelSetting]) { * Inactive/Active mode. */ inactiveMode(query, active = false) { - query.where('active', !active); + query.where('items.active', !active); }, }; } diff --git a/server/src/models/ItemCategory.Settings.ts b/server/src/models/ItemCategory.Settings.ts index a92ac7f16..c47a075c8 100644 --- a/server/src/models/ItemCategory.Settings.ts +++ b/server/src/models/ItemCategory.Settings.ts @@ -15,22 +15,11 @@ export default { column: 'description', fieldType: 'text', }, - cost_account: { - label: 'Cost account', - column: 'cost_account_id', - }, - sell_account: { - label: 'Sell account', - column: 'sell_account_id', - }, - inventory_account: { - label: 'Inventory account', - column: 'inventory_account_id', - }, count: { label: 'Count', column: 'count', - sortQuery: this.sortCountQuery, + fieldType: 'number', + virtualColumn: true, }, created_at: { label: 'Created at', diff --git a/server/src/models/ItemCategory.js b/server/src/models/ItemCategory.js index ce62877fa..ef11ada3f 100644 --- a/server/src/models/ItemCategory.js +++ b/server/src/models/ItemCategory.js @@ -1,8 +1,9 @@ -import path from 'path'; -import { Model } from 'objection'; +import { Model, mixin } from 'objection'; import TenantModel from 'models/TenantModel'; +import ModelSetting from './ModelSetting'; +import ItemCategorySettings from './ItemCategory.Settings'; -export default class ItemCategory extends TenantModel { +export default class ItemCategory extends mixin(TenantModel, [ModelSetting]) { /** * Table name. */ @@ -10,10 +11,6 @@ export default class ItemCategory extends TenantModel { return 'items_categories'; } - static get resourceable() { - return true; - } - /** * Timestamps columns. */ @@ -42,7 +39,24 @@ export default class ItemCategory extends TenantModel { }; } - static sortCountQuery(query, role) { - query.orderBy('count', role.order); - } + /** + * Model modifiers. + */ + static get modifiers() { + return { + /** + * Inactive/Active mode. + */ + sortByCount(query, order = 'asc') { + query.orderBy('count', order); + }, + }; + } + + /** + * Model meta. + */ + static get meta() { + return ItemCategorySettings; + } } diff --git a/server/src/models/ManualJournal.Settings.ts b/server/src/models/ManualJournal.Settings.ts index 1f57a9a0c..8579d90dc 100644 --- a/server/src/models/ManualJournal.Settings.ts +++ b/server/src/models/ManualJournal.Settings.ts @@ -39,7 +39,7 @@ export default { label: 'Status', column: 'status', fieldType: 'enumeration', - sortQuery: statusFieldSortQuery, + sortCustomQuery: StatusFieldSortQuery, }, 'created_at': { label: 'Created at', @@ -49,6 +49,6 @@ export default { }, }; -function statusFieldSortQuery(query, role) { +function StatusFieldSortQuery(query, role) { return query.modify('sortByStatus', role.order); } diff --git a/server/src/models/ModelSetting.ts b/server/src/models/ModelSetting.ts index 353a636c3..60b8196ff 100644 --- a/server/src/models/ModelSetting.ts +++ b/server/src/models/ModelSetting.ts @@ -15,8 +15,10 @@ export default (Model) => * @param {string} key * @returns {IModelMetaField} */ - public static getField(key: string): IModelMetaField { - return get(this.meta.fields, key); + public static getField(key: string, attribute?:string): IModelMetaField { + const field = get(this.meta.fields, key); + + return attribute ? get(field, attribute) : field; } /** diff --git a/server/src/models/PaymentReceive.Settings.ts b/server/src/models/PaymentReceive.Settings.ts index 4c99cbac8..da83a90a2 100644 --- a/server/src/models/PaymentReceive.Settings.ts +++ b/server/src/models/PaymentReceive.Settings.ts @@ -4,6 +4,13 @@ export default { customer: { name: 'Customer', column: 'customer_id', + fieldType: 'relation', + + relationType: 'enumeration', + relationKey: 'customer', + + relationEntityLabel: 'name', + relationEntityKey: 'id', }, payment_date: { name: 'Payment date', @@ -23,6 +30,13 @@ export default { deposit_account: { name: 'Deposit account', column: 'deposit_account_id', + fieldType: 'relation', + + relationType: 'enumeration', + relationKey: 'depositAccount', + + relationEntityLabel: 'name', + relationEntityKey: 'slug', }, payment_receive_no: { name: 'Payment receive No.', diff --git a/server/src/models/SaleEstimate.Settings.ts b/server/src/models/SaleEstimate.Settings.ts index 6feb44fe6..59f011850 100644 --- a/server/src/models/SaleEstimate.Settings.ts +++ b/server/src/models/SaleEstimate.Settings.ts @@ -18,6 +18,13 @@ export default { 'customer': { name: 'Customer', column: 'customer_id', + fieldType: 'relation', + + relationType: 'enumeration', + relationKey: 'customer', + + relationEntityLabel: 'display_name', + relationEntityKey: 'id', }, 'estimate_date': { name: 'Estimate date', @@ -46,8 +53,16 @@ export default { }, 'status': { name: 'Status', - filterQuery: statusFieldFilterQuery, - sortQuery: statusFieldSortQuery, + fieldType: 'enumeration', + options: [ + { name: 'Delivered', key: 'delivered' }, + { name: 'Rejected', key: 'rejected' }, + { name: 'Approved', key: 'approved' }, + { name: 'Delivered', key: 'delivered' }, + { name: 'Draft', key: 'draft' }, + ], + filterCustomQuery: StatusFieldFilterQuery, + sortCustomQuery: StatusFieldSortQuery, }, 'created_at': { name: 'Created at', @@ -57,29 +72,10 @@ export default { }, }; -function statusFieldSortQuery(query, role) { - return query.modify('orderByDraft', role.order); +function StatusFieldSortQuery(query, role) { + query.modify('orderByStatus', role.order); } -function statusFieldFilterQuery(query, role) { - switch (role.value) { - case 'draft': - query.modify('draft'); - break; - case 'delivered': - query.modify('delivered'); - break; - case 'approved': - query.modify('approved'); - break; - case 'rejected': - query.modify('rejected'); - break; - case 'invoiced': - query.modify('invoiced'); - break; - case 'expired': - query.modify('expired'); - break; - } +function StatusFieldFilterQuery(query, role) { + query.modify('filterByStatus', role.value); } diff --git a/server/src/models/SaleEstimate.js b/server/src/models/SaleEstimate.js index 728122c0a..473027af3 100644 --- a/server/src/models/SaleEstimate.js +++ b/server/src/models/SaleEstimate.js @@ -128,9 +128,34 @@ export default class SaleEstimate extends mixin(TenantModel, [ModelSetting]) { /** * Sorting the estimates orders by delivery status. */ - orderByDraft(query, order) { + orderByStatus(query, order) { query.orderByRaw(`delivered_at is null ${order}`); }, + /** + * Filtering the estimates oreders by status field. + */ + filterByStatus(query, filterType) { + switch (filterType) { + case 'draft': + query.modify('draft'); + break; + case 'delivered': + query.modify('delivered'); + break; + case 'approved': + query.modify('approved'); + break; + case 'rejected': + query.modify('rejected'); + break; + case 'invoiced': + query.modify('invoiced'); + break; + case 'expired': + query.modify('expired'); + break; + } + } }; } diff --git a/server/src/models/SaleInvoice.Settings.ts b/server/src/models/SaleInvoice.Settings.ts index 576752f3e..d87634d43 100644 --- a/server/src/models/SaleInvoice.Settings.ts +++ b/server/src/models/SaleInvoice.Settings.ts @@ -5,50 +5,50 @@ export default { sortField: 'created_at', }, fields: { - // customer: { - // name: 'Customer', - // column: 'customer_id', - // }, + customer: { + name: 'Customer', + column: 'customer_id', + fieldType: 'relation', + + relationType: 'enumeration', + relationKey: 'customer', + + relationEntityLabel: 'display_name', + relationEntityKey: 'id', + }, invoice_date: { name: 'Invoice date', column: 'invoice_date', fieldType: 'date', - columnable: true, }, due_date: { name: 'Due date', column: 'due_date', fieldType: 'date', - columnable: true, }, invoice_no: { name: 'Invoice No.', column: 'invoice_no', fieldType: 'text', - columnable: true, }, reference_no: { name: 'Reference No.', column: 'reference_no', fieldType: 'text', - columnable: true, }, invoice_message: { name: 'Invoice message', column: 'invoice_message', fieldType: 'text', - columnable: true, }, terms_conditions: { name: 'Terms & conditions', column: 'terms_conditions', fieldType: 'text', - columnable: true, }, amount: { name: 'Invoice amount', column: 'balance', - columnable: true, fieldType: 'number', }, payment_amount: { @@ -56,31 +56,45 @@ export default { column: 'payment_amount', fieldType: 'number', }, - due_amount: { + due_amount: { // calculated. name: 'Due amount', column: 'due_amount', fieldType: 'number', - // sortQuery: SaleInvoice.dueAmountFieldSortQuery, + virtualColumn: true, + }, + status: { + name: 'Status', + columnable: true, + fieldType: 'enumeration', + options: [ + { key: 'draft', name: 'Draft' }, + { key: 'delivered', name: 'Delivered' }, + { key: 'unpaid', name: 'Unpaid' }, + { key: 'overdue', name: 'Overdue' }, + { key: 'partially-paid', name: 'Partially paid' }, + { key: 'paid', name: 'Paid' }, + ], + filterCustomQuery: StatusFieldFilterQuery, + sortCustomQuery: StatusFieldSortQuery, }, created_at: { name: 'Created at', column: 'created_at', fieldType: 'date', - }, - // status: { - // name: 'Status', - // columnable: true, - // fieldType: 'enumeration', - // options: [ - // { key: 'draft', name: 'Draft' }, - // { key: 'delivered', name: 'Delivered' }, - // { key: 'unpaid', name: 'Unpaid' }, - // { key: 'overdue', name: 'Overdue' }, - // { key: 'partially-paid', name: 'Partially paid' }, - // { key: 'paid', name: 'Paid' }, - // ], - // // filterQuery: SaleInvoice.statusFieldFilterQuery, - // // sortQuery: SaleInvoice.statusFieldSortQuery, - // }, + }, }, }; + +/** + * Status field filter custom query. + */ +function StatusFieldFilterQuery(query, role) { + query.modify('statusFilter', role.value); +} + +/** + * Status field sort custom query. + */ +function StatusFieldSortQuery(query, role) { + query.modify('sortByStatus', role.order); +} \ No newline at end of file diff --git a/server/src/models/SaleInvoice.js b/server/src/models/SaleInvoice.js index a3b07418b..5abb5d005 100644 --- a/server/src/models/SaleInvoice.js +++ b/server/src/models/SaleInvoice.js @@ -108,10 +108,6 @@ export default class SaleInvoice extends mixin(TenantModel, [ModelSetting]) { return this.getOverdueDays(); } - static get resourceable() { - return true; - } - /** * * @param {*} asDate @@ -231,6 +227,33 @@ export default class SaleInvoice extends mixin(TenantModel, [ModelSetting]) { byPrefixAndNumber(query, prefix, number) { query.where('invoice_no', `${prefix}${number}`); }, + + /** + * Status filter. + */ + statusFilter(query, filterType) { + switch (filterType) { + case 'draft': + query.modify('draft'); + break; + case 'delivered': + query.modify('delivered'); + break; + case 'unpaid': + query.modify('unpaid'); + break; + case 'overdue': + default: + query.modify('overdue'); + break; + case 'partially-paid': + query.modify('partiallyPaid'); + break; + case 'paid': + query.modify('paid'); + break; + } + }, }; } @@ -240,7 +263,7 @@ export default class SaleInvoice extends mixin(TenantModel, [ModelSetting]) { static get relationMappings() { const AccountTransaction = require('models/AccountTransaction'); const ItemEntry = require('models/ItemEntry'); - const Contact = require('models/Contact'); + const Customer = require('models/Customer'); const InventoryCostLotTracker = require('models/InventoryCostLotTracker'); const PaymentReceiveEntry = require('models/PaymentReceiveEntry'); @@ -265,7 +288,7 @@ export default class SaleInvoice extends mixin(TenantModel, [ModelSetting]) { */ customer: { relation: Model.BelongsToOneRelation, - modelClass: Contact.default, + modelClass: Customer.default, join: { from: 'sales_invoices.customerId', to: 'contacts.id', @@ -336,31 +359,4 @@ export default class SaleInvoice extends mixin(TenantModel, [ModelSetting]) { static dueAmountFieldSortQuery(query, role) { query.modify('sortByDueAmount', role.order); } - - static statusFieldFilterQuery(query, role) { - switch (role.value) { - case 'draft': - query.modify('draft'); - break; - case 'delivered': - query.modify('delivered'); - break; - case 'unpaid': - query.modify('unpaid'); - break; - case 'overdue': - query.modify('overdue'); - break; - case 'partially-paid': - query.modify('partiallyPaid'); - break; - case 'paid': - query.modify('paid'); - break; - } - } - - static statusFieldSortQuery(query, role) { - query.modify('sortByStatus', role.order); - } } diff --git a/server/src/models/SaleReceipt.Settings.ts b/server/src/models/SaleReceipt.Settings.ts index 0ddc6153c..023a1c684 100644 --- a/server/src/models/SaleReceipt.Settings.ts +++ b/server/src/models/SaleReceipt.Settings.ts @@ -9,53 +9,59 @@ export default { name: 'Amount', column: 'amount', fieldType: 'number', - columnable: true }, 'deposit_account': { column: 'deposit_account_id', name: 'Deposit account', - columnable: true + fieldType: 'relation', + + relationType: 'enumeration', + relationKey: 'depositAccount', + + relationEntityLabel: 'name', + relationEntityKey: 'slug', }, 'customer': { name: 'Customer', column: 'customer_id', - columnable: true + fieldType: 'relation', + + relationType: 'enumeration', + relationKey: 'customer', + + relationEntityLabel: 'display_name', + relationEntityKey: 'id', }, 'receipt_date': { name: 'Receipt date', column: 'receipt_date', fieldType: 'date', - columnable: true + }, 'receipt_number': { name: 'Receipt No.', column: 'receipt_number', fieldType: 'text', - columnable: true }, 'reference_no': { name: 'Reference No.', column: 'reference_no', fieldType: 'text', - columnable: true }, 'receipt_message': { name: 'Receipt message', column: 'receipt_message', fieldType: 'text', - columnable: true }, 'statement': { name: 'Statement', column: 'statement', fieldType: 'text', - columnable: true }, 'created_at': { name: 'Created at', column: 'created_at', fieldType: 'date', - columnable: true }, 'status': { name: 'Status', @@ -64,24 +70,16 @@ export default { { key: 'draft', name: 'Draft' }, { key: 'closed', name: 'Closed' }, ], - query: statusFieldFilterQuery, - sortQuery: statusFieldSortQuery, - columnable: true + filterCustomQuery: StatusFieldFilterQuery, + sortCustomQuery: StatusFieldSortQuery, }, }, }; -function statusFieldFilterQuery(query, role) { - switch (role.value) { - case 'draft': - query.modify('draft'); - break; - case 'closed': - query.modify('closed'); - break; - } +function StatusFieldFilterQuery(query, role) { + query.modify('filterByStatus', role.value); } -function statusFieldSortQuery(query, role) { +function StatusFieldSortQuery(query, role) { query.modify('sortByStatus', role.order); } diff --git a/server/src/models/SaleReceipt.js b/server/src/models/SaleReceipt.js index ea226ad05..223565e36 100644 --- a/server/src/models/SaleReceipt.js +++ b/server/src/models/SaleReceipt.js @@ -66,6 +66,21 @@ export default class SaleReceipt extends mixin(TenantModel, [ModelSetting]) { sortByStatus(query, order) { query.orderByRaw(`CLOSED_AT IS NULL ${order}`); }, + + /** + * Filtering the receipts orders by status. + */ + filterByStatus(query, status) { + switch (status) { + case 'draft': + query.modify('draft'); + break; + case 'closed': + default: + query.modify('closed'); + break; + } + } }; } @@ -73,7 +88,7 @@ export default class SaleReceipt extends mixin(TenantModel, [ModelSetting]) { * Relationship mapping. */ static get relationMappings() { - const Contact = require('models/Contact'); + const Customer = require('models/Customer'); const Account = require('models/Account'); const AccountTransaction = require('models/AccountTransaction'); const ItemEntry = require('models/ItemEntry'); @@ -81,7 +96,7 @@ export default class SaleReceipt extends mixin(TenantModel, [ModelSetting]) { return { customer: { relation: Model.BelongsToOneRelation, - modelClass: Contact.default, + modelClass: Customer.default, join: { from: 'sales_receipts.customerId', to: 'contacts.id', diff --git a/server/src/services/DynamicListing/DynamicListService.ts b/server/src/services/DynamicListing/DynamicListService.ts index 94ee4ec0a..f6eeb0233 100644 --- a/server/src/services/DynamicListing/DynamicListService.ts +++ b/server/src/services/DynamicListing/DynamicListService.ts @@ -30,6 +30,8 @@ export default class DynamicListService implements IDynamicListService { /** * Parses filter DTO. + * @param {IMode} model - + * @param {} filterDTO - */ private parseFilterObject = (model, filterDTO) => { return { diff --git a/server/src/services/Inventory/InventoryAdjustmentService.ts b/server/src/services/Inventory/InventoryAdjustmentService.ts index 2b0b75da2..f352c3fec 100644 --- a/server/src/services/Inventory/InventoryAdjustmentService.ts +++ b/server/src/services/Inventory/InventoryAdjustmentService.ts @@ -268,8 +268,7 @@ export default class InventoryAdjustmentService { /** * Parses inventory adjustments list filter DTO. - * @param filterDTO - * @returns + * @param filterDTO - */ private parseListFilterDTO(filterDTO) { return R.compose( @@ -282,7 +281,7 @@ export default class InventoryAdjustmentService { * @param {number} tenantId * @param {IInventoryAdjustmentsFilter} adjustmentsFilter */ - async getInventoryAdjustments( + public async getInventoryAdjustments( tenantId: number, filterDTO: IInventoryAdjustmentsFilter ): Promise<{ diff --git a/server/src/services/ItemCategories/ItemCategoriesService.ts b/server/src/services/ItemCategories/ItemCategoriesService.ts index a53cd2e58..54598e28d 100644 --- a/server/src/services/ItemCategories/ItemCategoriesService.ts +++ b/server/src/services/ItemCategories/ItemCategoriesService.ts @@ -412,6 +412,7 @@ export default class ItemCategoriesService implements IItemCategoriesService { filter ); + // Items categories. const itemCategories = await ItemCategory.query().onBuild((query) => { // Subquery to calculate sumation of assocaited items to the item category. query.select('*', ItemCategory.relatedQuery('items').count().as('count'));