WIP: dynamic list filtering.

This commit is contained in:
a.bouhuolia
2021-07-31 17:07:37 +02:00
parent 9186076676
commit 3546b6b7ae
34 changed files with 569 additions and 314 deletions

View File

@@ -62,6 +62,8 @@ export default class InventoryAdjustmentsController extends BaseController {
query('page').optional().isNumeric().toInt(), query('page').optional().isNumeric().toInt(),
query('page_size').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(),
query('stringified_filter_roles').optional().isJSON(),
]; ];
} }

View File

@@ -59,12 +59,21 @@ export interface IModelMetaEnumerationField {
options: IModelMetaEnumerationOption[]; options: IModelMetaEnumerationOption[];
} }
export interface IModelMetaRelationField { export interface IModelMetaRelationFieldCommon {
fieldType: 'relation'; 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 { export interface IModelMeta {
defaultFilterField: string; defaultFilterField: string;
defaultSort: IModelMetaDefaultSort; defaultSort: IModelMetaDefaultSort;

View File

@@ -25,6 +25,9 @@ export default class DynamicFilter extends DynamicFilterAbstructor{
*/ */
public setFilter = (dynamicFilter: IDynamicFilter) => { public setFilter = (dynamicFilter: IDynamicFilter) => {
dynamicFilter.setModel(this.model); dynamicFilter.setModel(this.model);
dynamicFilter.onInitialize();
this.dynamicFilters.push(dynamicFilter); this.dynamicFilters.push(dynamicFilter);
} }
@@ -66,11 +69,7 @@ export default class DynamicFilter extends DynamicFilterAbstructor{
buildersCallbacks.forEach((builderCallback) => { buildersCallbacks.forEach((builderCallback) => {
builderCallback(builder); builderCallback(builder);
}); });
this.buildFilterRolesJoins(builder);
this.buildFilterRolesJoins(
this.model,
uniqBy(tableColumns, 'columnKey')
)(builder);
}; };
} }

View File

@@ -1,4 +1,5 @@
import { IModel, IFilterRole } from 'interfaces'; import { IModel, IFilterRole } from 'interfaces';
import { FIELD_TYPE } from './constants';
export default class DynamicFilterAbstructor { export default class DynamicFilterAbstructor {
/** /**
@@ -16,22 +17,24 @@ export default class DynamicFilterAbstructor {
* @param {String} tableName - Table name. * @param {String} tableName - Table name.
* @param {Array} roles - Roles. * @param {Array} roles - Roles.
*/ */
protected buildFilterRolesJoins = (model: IModel, roles: IFilterRole[]) => { protected buildFilterRolesJoins = (builder) => {
return (builder) => { this.dynamicFilters.forEach((dynamicFilter) => {
roles.forEach((role) => { const relationsFields = dynamicFilter.relationFields;
const field = model.getField(role.fieldKey);
if (field.relation) { this.buildFieldsJoinQueries(builder, relationsFields);
const joinTable = this.getTableFromRelationColumn(field.relation); });
};
builder.join( private buildFieldsJoinQueries = (builder, fieldsRelations: string[]) => {
joinTable, fieldsRelations.forEach((fieldRelation) => {
`${model.tableName}.${field.column}`, const relation = this.model.relationMappings[fieldRelation];
'=',
field.relation if (relation) {
); const splitToRelation = relation.join.to.split('.');
} const relationTable = splitToRelation[0] || '';
});
}; builder.join(relationTable, relation.join.from, '=', relation.join.to);
}
});
}; };
} }

View File

@@ -13,9 +13,14 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor {
super(); super();
this.filterRoles = filterRoles; this.filterRoles = filterRoles;
this.setResponseMeta(); this.setResponseMeta();
} }
public onInitialize() {
this.setFilterRolesRelations();
}
/** /**
* Builds filter roles logic expression. * Builds filter roles logic expression.
* @return {string} * @return {string}
@@ -53,4 +58,13 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor {
filterRoles: this.filterRoles, filterRoles: this.filterRoles,
}; };
} }
/**
* Sets filter roles relations if field was relation type.
*/
private setFilterRolesRelations() {
this.filterRoles.forEach((relationRole) => {
this.setRelationIfRelationField(relationRole.fieldKey);
});
}
} }

View File

@@ -12,6 +12,7 @@ export default abstract class DynamicFilterAbstructor
protected tableName: string; protected tableName: string;
protected model: IModel; protected model: IModel;
protected responseMeta: { [key: string]: any } = {}; protected responseMeta: { [key: string]: any } = {};
public relationFields = [];
/** /**
* Sets model the dynamic filter service. * 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. * Builds roles queries.
* @param {IModel} model - * @param {IModel} model -
* @param {Object} role - * @param {Object} role -
*/ */
protected buildRoleQuery = (model: IModel, role: IFilterRole) => { protected buildRoleQuery = (model: IModel, role: IFilterRole) => {
const fieldRelation = model.getField(role.fieldKey); const field = model.getField(role.fieldKey);
const comparatorColumn = `${model.tableName}.${fieldRelation.column}`; const comparatorColumn = this.getFieldComparatorColumn(field);
// Field relation custom query. // Field relation custom query.
if (typeof fieldRelation.customQuery !== 'undefined') { if (typeof field.filterCustomQuery !== 'undefined') {
return (builder) => { return (builder) => {
fieldRelation.customQuery(builder, role); field.filterCustomQuery(builder, role);
}; };
} }
switch (fieldRelation.fieldType) { switch (field.fieldType) {
case FIELD_TYPE.BOOLEAN: case FIELD_TYPE.BOOLEAN:
case FIELD_TYPE.ENUMERATION: case FIELD_TYPE.ENUMERATION:
return this.booleanRoleQueryBuilder(role, comparatorColumn); return this.booleanRoleQueryBuilder(role, comparatorColumn);
@@ -265,7 +294,7 @@ export default abstract class DynamicFilterAbstructor
builder.where(comparatorColumn, '>=', startDate.format(dateFormat)); builder.where(comparatorColumn, '>=', startDate.format(dateFormat));
builder.where(comparatorColumn, '<=', endDate.format(dateFormat)); builder.where(comparatorColumn, '<=', endDate.format(dateFormat));
} }
} };
/** /**
* Date query after/before comparator type. * Date query after/before comparator type.
@@ -296,5 +325,25 @@ export default abstract class DynamicFilterAbstructor
} }
const comparatorValue = targetDate.format(dateFormat); const comparatorValue = targetDate.format(dateFormat);
builder.where(comparatorColumn, comparator, comparatorValue); 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);
}
};
} }

View File

@@ -1,9 +1,5 @@
import DynamicFilterRoleAbstructor from 'lib/DynamicFilter/DynamicFilterRoleAbstructor'; import DynamicFilterRoleAbstructor from 'lib/DynamicFilter/DynamicFilterRoleAbstructor';
import { import { FIELD_TYPE } from './constants';
getRoleFieldColumn,
validateFieldKeyExistance,
getTableFromRelationColumn,
} from 'lib/ViewRolesBuilder';
interface ISortRole { interface ISortRole {
fieldKey: string; fieldKey: string;
@@ -28,17 +24,52 @@ export default class DynamicFilterSortBy extends DynamicFilterRoleAbstructor {
this.setResponseMeta(); 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. * Builds database query of sort by column on the given direction.
*/ */
public buildQuery() { public buildQuery = () => {
const field = this.model.getField(this.sortRole.fieldKey); 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) => { 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); 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. * Sets response meta.

View File

@@ -33,4 +33,5 @@ export const FIELD_TYPE = {
BOOLEAN: 'boolean', BOOLEAN: 'boolean',
RELATION: 'relation', RELATION: 'relation',
DATE: 'date', DATE: 'date',
COMPUTED: 'computed'
}; };

View File

@@ -1,4 +1,5 @@
import { IModelMeta } from 'interfaces'; import { IModelMeta } from 'interfaces';
import { ACCOUNT_TYPES } from 'data/AccountTypes';
export default { export default {
defaultFilterField: 'name', defaultFilterField: 'name',
@@ -10,25 +11,26 @@ export default {
name: { name: {
name: 'Account name', name: 'Account name',
column: 'name', column: 'name',
columnable: true,
fieldType: 'text', fieldType: 'text',
}, },
description: { description: {
name: 'Description', name: 'Description',
column: 'description', column: 'description',
columnable: true,
fieldType: 'text', fieldType: 'text',
}, },
slug: {
name: 'Account slug',
column: 'slug',
fieldType: 'text',
columnable: false,
},
code: { code: {
name: 'Account code', name: 'Account code',
column: 'code', column: 'code',
columnable: true,
fieldType: 'text', fieldType: 'text',
}, },
root_type: { root_type: {
name: 'Root type', name: 'Root type',
column: 'root_type',
columnable: true,
fieldType: 'enumeration', fieldType: 'enumeration',
options: [ options: [
{ key: 'asset', label: 'Asset' }, { key: 'asset', label: 'Asset' },
@@ -37,16 +39,37 @@ export default {
{ key: 'Income', label: 'Income' }, { key: 'Income', label: 'Income' },
{ key: 'expense', label: 'Expense' }, { 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: { active: {
name: 'Active', name: 'Active',
column: 'active', column: 'active',
fieldType: 'boolean', fieldType: 'boolean',
filterable: false,
}, },
amount: { balance: {
name: 'Account balance', name: 'Account balance',
column: 'amount', column: 'amount',
columnable: true,
fieldType: 'number', fieldType: 'number',
}, },
currency: { currency: {
@@ -60,4 +83,18 @@ export default {
fieldType: 'date', 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);
}

View File

@@ -8,6 +8,7 @@ import DependencyGraph from 'lib/DependencyGraph';
import AccountTypesUtils from 'lib/AccountTypes'; import AccountTypesUtils from 'lib/AccountTypes';
import AccountSettings from './Account.Settings'; import AccountSettings from './Account.Settings';
import ModelSettings from './ModelSetting'; import ModelSettings from './ModelSetting';
import { ACCOUNT_TYPES } from 'data/AccountTypes';
export default class Account extends mixin(TenantModel, [ModelSettings]) { export default class Account extends mixin(TenantModel, [ModelSettings]) {
/** /**
@@ -98,7 +99,7 @@ export default class Account extends mixin(TenantModel, [ModelSettings]) {
* Inactive/Active mode. * Inactive/Active mode.
*/ */
inactiveMode(query, active = false) { inactiveMode(query, active = false) {
query.where('active', !active); query.where('accounts.active', !active);
}, },
filterAccounts(query, accountIds) { filterAccounts(query, accountIds) {
@@ -117,6 +118,28 @@ export default class Account extends mixin(TenantModel, [ModelSettings]) {
sortColumnBuilder(query, columnKey, direction) { sortColumnBuilder(query, columnKey, direction) {
buildSortColumnQuery(Account.tableName, columnKey, direction)(query); 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);
},
}; };
} }

View File

@@ -8,35 +8,42 @@ export default {
sortField: 'bill_date', sortField: 'bill_date',
}, },
fields: { fields: {
// vendor: { vendor: {
// name: 'Vendor', name: 'Vendor',
// column: 'vendor_id', column: 'vendor_id',
// }, fieldType: 'relation',
'bill_number': {
relationType: 'enumeration',
relationKey: 'vendor',
relationEntityLabel: 'name',
relationEntityKey: 'id',
},
bill_number: {
name: 'Bill number', name: 'Bill number',
column: 'bill_number', column: 'bill_number',
columnable: true, columnable: true,
fieldType: 'text', fieldType: 'text',
}, },
'bill_date': { bill_date: {
name: 'Bill date', name: 'Bill date',
column: 'bill_date', column: 'bill_date',
columnable: true, columnable: true,
fieldType: 'date', fieldType: 'date',
}, },
'due_date': { due_date: {
name: 'Due date', name: 'Due date',
column: 'due_date', column: 'due_date',
columnable: true, columnable: true,
fieldType: 'date', fieldType: 'date',
}, },
'reference_no': { reference_no: {
name: 'Reference No.', name: 'Reference No.',
column: 'reference_no', column: 'reference_no',
columnable: true, columnable: true,
fieldType: 'text', fieldType: 'text',
}, },
'status': { status: {
name: 'Status', name: 'Status',
fieldType: 'enumeration', fieldType: 'enumeration',
columnable: true, columnable: true,
@@ -48,32 +55,42 @@ export default {
{ name: 'Opened', key: 'opened' }, { name: 'Opened', key: 'opened' },
{ name: 'Draft', key: 'draft' }, { name: 'Draft', key: 'draft' },
], ],
// filterQuery: Bill.statusFieldFilterQuery, filterCustomQuery: StatusFieldFilterQuery,
// sortQuery: Bill.statusFieldSortQuery, sortCustomQuery: StatusFieldSortQuery,
}, },
'amount': { amount: {
name: 'Amount', name: 'Amount',
column: 'amount', column: 'amount',
columnable: true,
fieldType: 'number', fieldType: 'number',
}, },
'payment_amount': { payment_amount: {
name: 'Payment amount', name: 'Payment amount',
column: 'payment_amount', column: 'payment_amount',
columnable: true,
fieldType: 'number', fieldType: 'number',
}, },
'note': { note: {
name: 'Note', name: 'Note',
column: 'note', column: 'note',
columnable: true,
fieldType: 'text', fieldType: 'text',
}, },
'created_at': { created_at: {
name: 'Created at', name: 'Created at',
column: 'created_at', column: 'created_at',
columnable: true,
fieldType: 'date', 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);
}

View File

@@ -71,15 +71,42 @@ export default class Bill extends mixin(TenantModel, [ModelSetting]) {
* Filters the bills from the given date. * Filters the bills from the given date.
*/ */
fromDate(query, fromDate) { fromDate(query, fromDate) {
query.where('bill_date', '<=', fromDate) query.where('bill_date', '<=', fromDate);
}, },
/** /**
* Sort the bills by full-payment bills. * Sort the bills by full-payment bills.
*/ */
sortByStatus(query, order) { sortByStatus(query, order) {
query.orderByRaw(`PAYMENT_AMOUNT = AMOUNT ${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', 'remainingDays',
'overdueDays', 'overdueDays',
'isOverdue', 'isOverdue',
'unallocatedCostAmount' 'unallocatedCostAmount',
]; ];
} }
@@ -276,32 +303,4 @@ export default class Bill extends mixin(TenantModel, [ModelSetting]) {
.where('id', billId) .where('id', billId)
[changeMethod]('payment_amount', Math.abs(amount)); [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);
};
} }

View File

@@ -1,4 +1,3 @@
import { IModelMeta } from 'interfaces';
export default { export default {
defaultFilterField: 'vendor', defaultFilterField: 'vendor',
@@ -10,51 +9,58 @@ export default {
'vendor': { 'vendor': {
name: 'Vendor name', name: 'Vendor name',
column: 'vendor_id', column: 'vendor_id',
fieldType: 'relation',
relationType: 'enumeration',
relationKey: 'vendor',
relationEntityLabel: 'display_name',
relationEntityKey: 'id',
}, },
'amount': { 'amount': {
name: 'Amount', name: 'Amount',
column: 'amount', column: 'amount',
columnable: true,
fieldType: 'number', fieldType: 'number',
}, },
'due_amount': { 'due_amount': {
name: 'Due amount', name: 'Due amount',
column: 'due_amount', column: 'due_amount',
columnable: true,
fieldType: 'number', fieldType: 'number',
}, },
'payment_account': { 'payment_account': {
name: 'Payment account', name: 'Payment account',
column: 'payment_account_id', column: 'payment_account_id',
fieldType: 'relation',
fieldRelation: 'paymentAccount',
fieldRelationType: 'enumeration',
relationLabelField: 'name',
relationKeyField: 'slug',
}, },
'payment_number': { 'payment_number': {
name: 'Payment number', name: 'Payment number',
column: 'payment_number', column: 'payment_number',
columnable: true,
fieldType: 'number', fieldType: 'number',
}, },
'payment_date': { 'payment_date': {
name: 'Payment date', name: 'Payment date',
column: 'payment_date', column: 'payment_date',
columnable: true,
fieldType: 'date', fieldType: 'date',
}, },
'reference_no': { 'reference_no': {
name: 'Reference No.', name: 'Reference No.',
column: 'reference', column: 'reference',
columnable: true,
fieldType: 'text', fieldType: 'text',
}, },
'description': { 'description': {
name: 'Description', name: 'Description',
column: 'description', column: 'description',
columnable: true,
fieldType: 'text', fieldType: 'text',
}, },
'created_at': { 'created_at': {
name: 'Created at', name: 'Created at',
column: 'created_at', column: 'created_at',
columnable: true,
fieldType: 'date', fieldType: 'date',
}, },
}, },

View File

@@ -31,7 +31,7 @@ export default class BillPayment extends mixin(TenantModel, [ModelSetting]) {
static get relationMappings() { static get relationMappings() {
const BillPaymentEntry = require("models/BillPaymentEntry"); const BillPaymentEntry = require("models/BillPaymentEntry");
const AccountTransaction = require("models/AccountTransaction"); const AccountTransaction = require("models/AccountTransaction");
const Contact = require("models/Contact"); const Vendor = require("models/Vendor");
const Account = require("models/Account"); const Account = require("models/Account");
return { return {
@@ -46,7 +46,7 @@ export default class BillPayment extends mixin(TenantModel, [ModelSetting]) {
vendor: { vendor: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: Contact.default, modelClass: Vendor.default,
join: { join: {
from: "bills_payments.vendorId", from: "bills_payments.vendorId",
to: "contacts.id", to: "contacts.id",

View File

@@ -16,23 +16,19 @@ export default {
'payment_account': { 'payment_account': {
name: 'Payment account', name: 'Payment account',
column: 'payment_account_id', column: 'payment_account_id',
fieldType: 'relation', fieldType: 'relation',
fieldRelation: 'paymentAccount',
fieldRelationType: 'enumeration', relationType: 'enumeration',
relationLabelField: 'name', relationKey: 'paymentAccount',
relationKeyField: 'slug',
relationEntityLabel: 'name',
relationEntityKey: 'slug',
}, },
'amount': { 'amount': {
name: 'Amount', name: 'Amount',
column: 'total_amount', column: 'total_amount',
fieldType: 'number', fieldType: 'number',
}, },
// currency_code: {
// name: 'Currency',
// column: 'currency_code',
// },
'reference_no': { 'reference_no': {
name: 'Reference No.', name: 'Reference No.',
column: 'reference_no', column: 'reference_no',
@@ -55,8 +51,8 @@ export default {
{ key: 'draft', name: 'Draft' }, { key: 'draft', name: 'Draft' },
{ key: 'published', name: 'Published' }, { key: 'published', name: 'Published' },
], ],
filterQuery: statusFieldFilterQuery, filterCustomQuery: StatusFieldFilterQuery,
sortQuery: statusFieldSortQuery, sortCustomQuery: StatusFieldSortQuery,
}, },
'created_at': { 'created_at': {
name: 'Created at', name: 'Created at',
@@ -66,17 +62,10 @@ export default {
}, },
}; };
function statusFieldFilterQuery(query, role) { function StatusFieldFilterQuery(query, role) {
switch (role.value) { query.modify('filterByStatus', role.value);
case 'draft':
query.modify('filterByDraft');
break;
case 'published':
query.modify('filterByPublished');
break;
}
} }
function statusFieldSortQuery(query, role) { function StatusFieldSortQuery(query, role) {
return query.modify('sortByStatus', role.order); query.modify('sortByStatus', role.order);
} }

View File

@@ -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() { static get virtualAttributes() {
return ['isPublished', 'unallocatedCostAmount']; return ['isPublished', 'unallocatedCostAmount'];
} }
@@ -92,6 +87,18 @@ export default class Expense extends mixin(TenantModel, [ModelSetting]) {
filterByPublished(query) { filterByPublished(query) {
query.whereNot('published_at', null); query.whereNot('published_at', null);
}, },
filterByStatus(query, status) {
switch (status) {
case 'draft':
query.modify('filterByDraft');
break;
case 'published':
default:
query.modify('filterByPublished');
break;
}
},
}; };
} }

View File

@@ -9,7 +9,6 @@ export default {
name: 'Date', name: 'Date',
column: 'date', column: 'date',
fieldType: 'date', fieldType: 'date',
columnable: true,
}, },
'type': { 'type': {
name: 'Adjustment type', name: 'Adjustment type',
@@ -19,42 +18,42 @@ export default {
{ key: 'increment', name: 'Increment' }, { key: 'increment', name: 'Increment' },
{ key: 'decrement', name: 'Decrement' }, { key: 'decrement', name: 'Decrement' },
], ],
columnable: true,
}, },
'adjustment_account': { 'adjustment_account': {
name: 'Adjustment account', name: 'Adjustment account',
column: 'adjustment_account_id', column: 'adjustment_account_id',
columnable: true, fieldType: 'relation',
relationType: 'enumeration',
relationKey: 'adjustmentAccount',
relationEntityLabel: 'name',
relationEntityKey: 'slug',
}, },
'reason': { 'reason': {
name: 'Reason', name: 'Reason',
column: 'reason', column: 'reason',
fieldType: 'text', fieldType: 'text',
columnable: true,
}, },
'reference_no': { 'reference_no': {
name: 'Reference No.', name: 'Reference No.',
column: 'reference_no', column: 'reference_no',
fieldType: 'text', fieldType: 'text',
columnable: true,
}, },
'description': { 'description': {
name: 'Description', name: 'Description',
column: 'description', column: 'description',
fieldType: 'text', fieldType: 'text',
columnable: true,
}, },
'published_at': { 'published_at': {
name: 'Published at', name: 'Published at',
column: 'published_at', column: 'published_at',
fieldType: 'date', fieldType: 'date',
columnable: true,
}, },
'created_at': { 'created_at': {
name: 'Created at', name: 'Created at',
column: 'created_at', column: 'created_at',
fieldType: 'date', fieldType: 'date',
columnable: true,
}, },
}, },
}; };

View File

@@ -1,8 +1,9 @@
import { Model } from 'objection'; import { Model, mixin } from 'objection';
import TenantModel from 'models/TenantModel'; import TenantModel from 'models/TenantModel';
import InventoryAdjustmentSettings from './InventoryAdjustment.Settings'; 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 * Table name
*/ */

View File

@@ -8,7 +8,6 @@ export default {
'type': { 'type': {
name: 'Item type', name: 'Item type',
column: 'type', column: 'type',
columnable: true,
fieldType: 'enumeration', fieldType: 'enumeration',
options: [ options: [
{ key: 'inventory', label: 'Inventory', }, { key: 'inventory', label: 'Inventory', },
@@ -19,13 +18,11 @@ export default {
'name': { 'name': {
name: 'Name', name: 'Name',
column: 'name', column: 'name',
columnable: true,
fieldType: 'text', fieldType: 'text',
}, },
'code': { 'code': {
name: 'Code', name: 'Code',
column: 'code', column: 'code',
columnable: true,
fieldType: 'text', fieldType: 'text',
}, },
'sellable': { 'sellable': {
@@ -51,15 +48,34 @@ export default {
'cost_account': { 'cost_account': {
name: 'Cost account', name: 'Cost account',
column: 'cost_account_id', column: 'cost_account_id',
columnable: true, fieldType: 'relation',
relationType: 'enumeration',
relationKey: 'costAccount',
relationEntityLabel: 'name',
relationEntityKey: 'slug',
}, },
'sell_account': { 'sell_account': {
name: 'Sell account', name: 'Sell account',
column: 'sell_account_id', column: 'sell_account_id',
fieldType: 'relation',
relationType: 'enumeration',
relationKey: 'sellAccount',
relationEntityLabel: 'name',
relationEntityKey: 'slug',
}, },
'inventory_account': { 'inventory_account': {
name: 'Inventory account', name: 'Inventory account',
column: 'inventory_account_id', column: 'inventory_account_id',
relationType: 'enumeration',
relationKey: 'inventoryAccount',
relationEntityLabel: 'name',
relationEntityKey: 'slug',
}, },
'sell_description': { 'sell_description': {
name: 'Sell description', name: 'Sell description',
@@ -80,17 +96,22 @@ export default {
name: 'Note', name: 'Note',
column: 'note', column: 'note',
fieldType: 'text', fieldType: 'text',
columnable: true,
}, },
'category': { 'category': {
name: 'Category', name: 'Category',
column: 'category_id', column: 'category_id',
columnable: true,
relationType: 'enumeration',
relationKey: 'category',
relationEntityLabel: 'name',
relationEntityKey: 'id',
}, },
'active': { 'active': {
name: 'Active', name: 'Active',
column: 'active', column: 'active',
fieldType: 'boolean', fieldType: 'boolean',
filterable: false,
}, },
'created_at': { 'created_at': {
name: 'Created at', name: 'Created at',

View File

@@ -42,7 +42,7 @@ export default class Item extends mixin(TenantModel, [ModelSetting]) {
* Inactive/Active mode. * Inactive/Active mode.
*/ */
inactiveMode(query, active = false) { inactiveMode(query, active = false) {
query.where('active', !active); query.where('items.active', !active);
}, },
}; };
} }

View File

@@ -15,22 +15,11 @@ export default {
column: 'description', column: 'description',
fieldType: 'text', 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: { count: {
label: 'Count', label: 'Count',
column: 'count', column: 'count',
sortQuery: this.sortCountQuery, fieldType: 'number',
virtualColumn: true,
}, },
created_at: { created_at: {
label: 'Created at', label: 'Created at',

View File

@@ -1,8 +1,9 @@
import path from 'path'; import { Model, mixin } from 'objection';
import { Model } from 'objection';
import TenantModel from 'models/TenantModel'; 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. * Table name.
*/ */
@@ -10,10 +11,6 @@ export default class ItemCategory extends TenantModel {
return 'items_categories'; return 'items_categories';
} }
static get resourceable() {
return true;
}
/** /**
* Timestamps columns. * 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;
}
} }

View File

@@ -39,7 +39,7 @@ export default {
label: 'Status', label: 'Status',
column: 'status', column: 'status',
fieldType: 'enumeration', fieldType: 'enumeration',
sortQuery: statusFieldSortQuery, sortCustomQuery: StatusFieldSortQuery,
}, },
'created_at': { 'created_at': {
label: '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); return query.modify('sortByStatus', role.order);
} }

View File

@@ -15,8 +15,10 @@ export default (Model) =>
* @param {string} key * @param {string} key
* @returns {IModelMetaField} * @returns {IModelMetaField}
*/ */
public static getField(key: string): IModelMetaField { public static getField(key: string, attribute?:string): IModelMetaField {
return get(this.meta.fields, key); const field = get(this.meta.fields, key);
return attribute ? get(field, attribute) : field;
} }
/** /**

View File

@@ -4,6 +4,13 @@ export default {
customer: { customer: {
name: 'Customer', name: 'Customer',
column: 'customer_id', column: 'customer_id',
fieldType: 'relation',
relationType: 'enumeration',
relationKey: 'customer',
relationEntityLabel: 'name',
relationEntityKey: 'id',
}, },
payment_date: { payment_date: {
name: 'Payment date', name: 'Payment date',
@@ -23,6 +30,13 @@ export default {
deposit_account: { deposit_account: {
name: 'Deposit account', name: 'Deposit account',
column: 'deposit_account_id', column: 'deposit_account_id',
fieldType: 'relation',
relationType: 'enumeration',
relationKey: 'depositAccount',
relationEntityLabel: 'name',
relationEntityKey: 'slug',
}, },
payment_receive_no: { payment_receive_no: {
name: 'Payment receive No.', name: 'Payment receive No.',

View File

@@ -18,6 +18,13 @@ export default {
'customer': { 'customer': {
name: 'Customer', name: 'Customer',
column: 'customer_id', column: 'customer_id',
fieldType: 'relation',
relationType: 'enumeration',
relationKey: 'customer',
relationEntityLabel: 'display_name',
relationEntityKey: 'id',
}, },
'estimate_date': { 'estimate_date': {
name: 'Estimate date', name: 'Estimate date',
@@ -46,8 +53,16 @@ export default {
}, },
'status': { 'status': {
name: 'Status', name: 'Status',
filterQuery: statusFieldFilterQuery, fieldType: 'enumeration',
sortQuery: statusFieldSortQuery, 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': { 'created_at': {
name: 'Created at', name: 'Created at',
@@ -57,29 +72,10 @@ export default {
}, },
}; };
function statusFieldSortQuery(query, role) { function StatusFieldSortQuery(query, role) {
return query.modify('orderByDraft', role.order); query.modify('orderByStatus', role.order);
} }
function statusFieldFilterQuery(query, role) { function StatusFieldFilterQuery(query, role) {
switch (role.value) { query.modify('filterByStatus', 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;
}
} }

View File

@@ -128,9 +128,34 @@ export default class SaleEstimate extends mixin(TenantModel, [ModelSetting]) {
/** /**
* Sorting the estimates orders by delivery status. * Sorting the estimates orders by delivery status.
*/ */
orderByDraft(query, order) { orderByStatus(query, order) {
query.orderByRaw(`delivered_at is null ${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;
}
}
}; };
} }

View File

@@ -5,50 +5,50 @@ export default {
sortField: 'created_at', sortField: 'created_at',
}, },
fields: { fields: {
// customer: { customer: {
// name: 'Customer', name: 'Customer',
// column: 'customer_id', column: 'customer_id',
// }, fieldType: 'relation',
relationType: 'enumeration',
relationKey: 'customer',
relationEntityLabel: 'display_name',
relationEntityKey: 'id',
},
invoice_date: { invoice_date: {
name: 'Invoice date', name: 'Invoice date',
column: 'invoice_date', column: 'invoice_date',
fieldType: 'date', fieldType: 'date',
columnable: true,
}, },
due_date: { due_date: {
name: 'Due date', name: 'Due date',
column: 'due_date', column: 'due_date',
fieldType: 'date', fieldType: 'date',
columnable: true,
}, },
invoice_no: { invoice_no: {
name: 'Invoice No.', name: 'Invoice No.',
column: 'invoice_no', column: 'invoice_no',
fieldType: 'text', fieldType: 'text',
columnable: true,
}, },
reference_no: { reference_no: {
name: 'Reference No.', name: 'Reference No.',
column: 'reference_no', column: 'reference_no',
fieldType: 'text', fieldType: 'text',
columnable: true,
}, },
invoice_message: { invoice_message: {
name: 'Invoice message', name: 'Invoice message',
column: 'invoice_message', column: 'invoice_message',
fieldType: 'text', fieldType: 'text',
columnable: true,
}, },
terms_conditions: { terms_conditions: {
name: 'Terms & conditions', name: 'Terms & conditions',
column: 'terms_conditions', column: 'terms_conditions',
fieldType: 'text', fieldType: 'text',
columnable: true,
}, },
amount: { amount: {
name: 'Invoice amount', name: 'Invoice amount',
column: 'balance', column: 'balance',
columnable: true,
fieldType: 'number', fieldType: 'number',
}, },
payment_amount: { payment_amount: {
@@ -56,31 +56,45 @@ export default {
column: 'payment_amount', column: 'payment_amount',
fieldType: 'number', fieldType: 'number',
}, },
due_amount: { due_amount: { // calculated.
name: 'Due amount', name: 'Due amount',
column: 'due_amount', column: 'due_amount',
fieldType: 'number', 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: { created_at: {
name: 'Created at', name: 'Created at',
column: 'created_at', column: 'created_at',
fieldType: 'date', 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);
}

View File

@@ -108,10 +108,6 @@ export default class SaleInvoice extends mixin(TenantModel, [ModelSetting]) {
return this.getOverdueDays(); return this.getOverdueDays();
} }
static get resourceable() {
return true;
}
/** /**
* *
* @param {*} asDate * @param {*} asDate
@@ -231,6 +227,33 @@ export default class SaleInvoice extends mixin(TenantModel, [ModelSetting]) {
byPrefixAndNumber(query, prefix, number) { byPrefixAndNumber(query, prefix, number) {
query.where('invoice_no', `${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() { static get relationMappings() {
const AccountTransaction = require('models/AccountTransaction'); const AccountTransaction = require('models/AccountTransaction');
const ItemEntry = require('models/ItemEntry'); const ItemEntry = require('models/ItemEntry');
const Contact = require('models/Contact'); const Customer = require('models/Customer');
const InventoryCostLotTracker = require('models/InventoryCostLotTracker'); const InventoryCostLotTracker = require('models/InventoryCostLotTracker');
const PaymentReceiveEntry = require('models/PaymentReceiveEntry'); const PaymentReceiveEntry = require('models/PaymentReceiveEntry');
@@ -265,7 +288,7 @@ export default class SaleInvoice extends mixin(TenantModel, [ModelSetting]) {
*/ */
customer: { customer: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: Contact.default, modelClass: Customer.default,
join: { join: {
from: 'sales_invoices.customerId', from: 'sales_invoices.customerId',
to: 'contacts.id', to: 'contacts.id',
@@ -336,31 +359,4 @@ export default class SaleInvoice extends mixin(TenantModel, [ModelSetting]) {
static dueAmountFieldSortQuery(query, role) { static dueAmountFieldSortQuery(query, role) {
query.modify('sortByDueAmount', role.order); 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);
}
} }

View File

@@ -9,53 +9,59 @@ export default {
name: 'Amount', name: 'Amount',
column: 'amount', column: 'amount',
fieldType: 'number', fieldType: 'number',
columnable: true
}, },
'deposit_account': { 'deposit_account': {
column: 'deposit_account_id', column: 'deposit_account_id',
name: 'Deposit account', name: 'Deposit account',
columnable: true fieldType: 'relation',
relationType: 'enumeration',
relationKey: 'depositAccount',
relationEntityLabel: 'name',
relationEntityKey: 'slug',
}, },
'customer': { 'customer': {
name: 'Customer', name: 'Customer',
column: 'customer_id', column: 'customer_id',
columnable: true fieldType: 'relation',
relationType: 'enumeration',
relationKey: 'customer',
relationEntityLabel: 'display_name',
relationEntityKey: 'id',
}, },
'receipt_date': { 'receipt_date': {
name: 'Receipt date', name: 'Receipt date',
column: 'receipt_date', column: 'receipt_date',
fieldType: 'date', fieldType: 'date',
columnable: true
}, },
'receipt_number': { 'receipt_number': {
name: 'Receipt No.', name: 'Receipt No.',
column: 'receipt_number', column: 'receipt_number',
fieldType: 'text', fieldType: 'text',
columnable: true
}, },
'reference_no': { 'reference_no': {
name: 'Reference No.', name: 'Reference No.',
column: 'reference_no', column: 'reference_no',
fieldType: 'text', fieldType: 'text',
columnable: true
}, },
'receipt_message': { 'receipt_message': {
name: 'Receipt message', name: 'Receipt message',
column: 'receipt_message', column: 'receipt_message',
fieldType: 'text', fieldType: 'text',
columnable: true
}, },
'statement': { 'statement': {
name: 'Statement', name: 'Statement',
column: 'statement', column: 'statement',
fieldType: 'text', fieldType: 'text',
columnable: true
}, },
'created_at': { 'created_at': {
name: 'Created at', name: 'Created at',
column: 'created_at', column: 'created_at',
fieldType: 'date', fieldType: 'date',
columnable: true
}, },
'status': { 'status': {
name: 'Status', name: 'Status',
@@ -64,24 +70,16 @@ export default {
{ key: 'draft', name: 'Draft' }, { key: 'draft', name: 'Draft' },
{ key: 'closed', name: 'Closed' }, { key: 'closed', name: 'Closed' },
], ],
query: statusFieldFilterQuery, filterCustomQuery: StatusFieldFilterQuery,
sortQuery: statusFieldSortQuery, sortCustomQuery: StatusFieldSortQuery,
columnable: true
}, },
}, },
}; };
function statusFieldFilterQuery(query, role) { function StatusFieldFilterQuery(query, role) {
switch (role.value) { query.modify('filterByStatus', role.value);
case 'draft':
query.modify('draft');
break;
case 'closed':
query.modify('closed');
break;
}
} }
function statusFieldSortQuery(query, role) { function StatusFieldSortQuery(query, role) {
query.modify('sortByStatus', role.order); query.modify('sortByStatus', role.order);
} }

View File

@@ -66,6 +66,21 @@ export default class SaleReceipt extends mixin(TenantModel, [ModelSetting]) {
sortByStatus(query, order) { sortByStatus(query, order) {
query.orderByRaw(`CLOSED_AT IS NULL ${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. * Relationship mapping.
*/ */
static get relationMappings() { static get relationMappings() {
const Contact = require('models/Contact'); const Customer = require('models/Customer');
const Account = require('models/Account'); const Account = require('models/Account');
const AccountTransaction = require('models/AccountTransaction'); const AccountTransaction = require('models/AccountTransaction');
const ItemEntry = require('models/ItemEntry'); const ItemEntry = require('models/ItemEntry');
@@ -81,7 +96,7 @@ export default class SaleReceipt extends mixin(TenantModel, [ModelSetting]) {
return { return {
customer: { customer: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: Contact.default, modelClass: Customer.default,
join: { join: {
from: 'sales_receipts.customerId', from: 'sales_receipts.customerId',
to: 'contacts.id', to: 'contacts.id',

View File

@@ -30,6 +30,8 @@ export default class DynamicListService implements IDynamicListService {
/** /**
* Parses filter DTO. * Parses filter DTO.
* @param {IMode} model -
* @param {} filterDTO -
*/ */
private parseFilterObject = (model, filterDTO) => { private parseFilterObject = (model, filterDTO) => {
return { return {

View File

@@ -268,8 +268,7 @@ export default class InventoryAdjustmentService {
/** /**
* Parses inventory adjustments list filter DTO. * Parses inventory adjustments list filter DTO.
* @param filterDTO * @param filterDTO -
* @returns
*/ */
private parseListFilterDTO(filterDTO) { private parseListFilterDTO(filterDTO) {
return R.compose( return R.compose(
@@ -282,7 +281,7 @@ export default class InventoryAdjustmentService {
* @param {number} tenantId * @param {number} tenantId
* @param {IInventoryAdjustmentsFilter} adjustmentsFilter * @param {IInventoryAdjustmentsFilter} adjustmentsFilter
*/ */
async getInventoryAdjustments( public async getInventoryAdjustments(
tenantId: number, tenantId: number,
filterDTO: IInventoryAdjustmentsFilter filterDTO: IInventoryAdjustmentsFilter
): Promise<{ ): Promise<{

View File

@@ -412,6 +412,7 @@ export default class ItemCategoriesService implements IItemCategoriesService {
filter filter
); );
// Items categories.
const itemCategories = await ItemCategory.query().onBuild((query) => { const itemCategories = await ItemCategory.query().onBuild((query) => {
// Subquery to calculate sumation of assocaited items to the item category. // Subquery to calculate sumation of assocaited items to the item category.
query.select('*', ItemCategory.relatedQuery('items').count().as('count')); query.select('*', ItemCategory.relatedQuery('items').count().as('count'));