diff --git a/packages/server/src/api/controllers/Accounts.ts b/packages/server/src/api/controllers/Accounts.ts index 94c673293..7d5b0271a 100644 --- a/packages/server/src/api/controllers/Accounts.ts +++ b/packages/server/src/api/controllers/Accounts.ts @@ -9,6 +9,7 @@ import DynamicListingService from '@/services/DynamicListing/DynamicListService' import { DATATYPES_LENGTH } from '@/data/DataTypes'; import CheckPolicies from '@/api/middleware/CheckPolicies'; import { AccountsApplication } from '@/services/Accounts/AccountsApplication'; +import { MAX_ACCOUNTS_CHART_DEPTH } from 'services/Accounts/constants'; @Service() export default class AccountsController extends BaseController { @@ -494,6 +495,22 @@ export default class AccountsController extends BaseController { } ); } + if (error.errorType === 'PARENT_ACCOUNT_EXCEEDED_THE_DEPTH_LEVEL') { + return res.boom.badRequest( + 'The parent account exceeded the depth level of accounts chart.', + { + errors: [ + { + type: 'PARENT_ACCOUNT_EXCEEDED_THE_DEPTH_LEVEL', + code: 1500, + data: { + maxDepth: MAX_ACCOUNTS_CHART_DEPTH, + }, + }, + ], + } + ); + } } next(error); } diff --git a/packages/server/src/services/Accounts/CommandAccountValidators.ts b/packages/server/src/services/Accounts/CommandAccountValidators.ts index e499c51fa..2819c7fdd 100644 --- a/packages/server/src/services/Accounts/CommandAccountValidators.ts +++ b/packages/server/src/services/Accounts/CommandAccountValidators.ts @@ -3,7 +3,7 @@ import TenancyService from '@/services/Tenancy/TenancyService'; import { ServiceError } from '@/exceptions'; import { IAccountDTO, IAccount, IAccountCreateDTO } from '@/interfaces'; import AccountTypesUtils from '@/lib/AccountTypes'; -import { ERRORS } from './constants'; +import { ERRORS, MAX_ACCOUNTS_CHART_DEPTH } from './constants'; @Service() export class CommandAccountValidators { @@ -154,13 +154,13 @@ export class CommandAccountValidators { * parent account. * @param {IAccountCreateDTO} accountDTO * @param {IAccount} parentAccount - * @param {string} baseCurrency - + * @param {string} baseCurrency - * @throws {ServiceError(ERRORS.ACCOUNT_CURRENCY_NOT_SAME_PARENT_ACCOUNT)} */ public validateCurrentSameParentAccount = ( accountDTO: IAccountCreateDTO, parentAccount: IAccount, - baseCurrency: string, + baseCurrency: string ) => { // If the account DTO currency not assigned and the parent account has no base currency. if ( @@ -208,4 +208,24 @@ export class CommandAccountValidators { } return account; } + + /** + * Validates the max depth level of accounts chart. + * @param {numebr} tenantId - Tenant id. + * @param {number} parentAccountId - Parent account id. + */ + public async validateMaxParentAccountDepthLevels( + tenantId: number, + parentAccountId: number + ) { + const { accountRepository } = this.tenancy.repositories(tenantId); + + const accountsGraph = await accountRepository.getDependencyGraph(); + + const parentDependantsIds = accountsGraph.dependantsOf(parentAccountId); + + if (parentDependantsIds.length >= MAX_ACCOUNTS_CHART_DEPTH) { + throw new ServiceError(ERRORS.PARENT_ACCOUNT_EXCEEDED_THE_DEPTH_LEVEL); + } + } } diff --git a/packages/server/src/services/Accounts/CreateAccount.ts b/packages/server/src/services/Accounts/CreateAccount.ts index fc9342a80..b621d9104 100644 --- a/packages/server/src/services/Accounts/CreateAccount.ts +++ b/packages/server/src/services/Accounts/CreateAccount.ts @@ -70,6 +70,11 @@ export class CreateAccount { parentAccount, baseCurrency ); + // Validates the max depth level of accounts chart. + await this.validator.validateMaxParentAccountDepthLevels( + tenantId, + accountDTO.parentAccountId + ); } // Validates the given account type supports the multi-currency. this.validator.validateAccountTypeSupportCurrency(accountDTO, baseCurrency); diff --git a/packages/server/src/services/Accounts/constants.ts b/packages/server/src/services/Accounts/constants.ts index 640fdf932..deab658bc 100644 --- a/packages/server/src/services/Accounts/constants.ts +++ b/packages/server/src/services/Accounts/constants.ts @@ -13,8 +13,12 @@ export const ERRORS = { CLOSE_ACCOUNT_AND_TO_ACCOUNT_NOT_SAME_TYPE: 'close_account_and_to_account_not_same_type', ACCOUNTS_NOT_FOUND: 'accounts_not_found', - ACCOUNT_TYPE_NOT_SUPPORTS_MULTI_CURRENCY: 'ACCOUNT_TYPE_NOT_SUPPORTS_MULTI_CURRENCY', - ACCOUNT_CURRENCY_NOT_SAME_PARENT_ACCOUNT: 'ACCOUNT_CURRENCY_NOT_SAME_PARENT_ACCOUNT', + ACCOUNT_TYPE_NOT_SUPPORTS_MULTI_CURRENCY: + 'ACCOUNT_TYPE_NOT_SUPPORTS_MULTI_CURRENCY', + ACCOUNT_CURRENCY_NOT_SAME_PARENT_ACCOUNT: + 'ACCOUNT_CURRENCY_NOT_SAME_PARENT_ACCOUNT', + PARENT_ACCOUNT_EXCEEDED_THE_DEPTH_LEVEL: + 'PARENT_ACCOUNT_EXCEEDED_THE_DEPTH_LEVEL', }; // Default views columns. @@ -27,6 +31,8 @@ export const DEFAULT_VIEW_COLUMNS = [ { key: 'currencyCode', label: 'Currency' }, ]; +export const MAX_ACCOUNTS_CHART_DEPTH = 5; + // Accounts default views. export const DEFAULT_VIEWS = [ { @@ -43,7 +49,12 @@ export const DEFAULT_VIEWS = [ slug: 'liabilities', rolesLogicExpression: '1', roles: [ - { fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'liability' }, + { + fieldKey: 'root_type', + index: 1, + comparator: 'equals', + value: 'liability', + }, ], columns: DEFAULT_VIEW_COLUMNS, }, @@ -52,7 +63,12 @@ export const DEFAULT_VIEWS = [ slug: 'equity', rolesLogicExpression: '1', roles: [ - { fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'equity' }, + { + fieldKey: 'root_type', + index: 1, + comparator: 'equals', + value: 'equity', + }, ], columns: DEFAULT_VIEW_COLUMNS, }, @@ -61,7 +77,12 @@ export const DEFAULT_VIEWS = [ slug: 'income', rolesLogicExpression: '1', roles: [ - { fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'income' }, + { + fieldKey: 'root_type', + index: 1, + comparator: 'equals', + value: 'income', + }, ], columns: DEFAULT_VIEW_COLUMNS, }, @@ -70,7 +91,12 @@ export const DEFAULT_VIEWS = [ slug: 'expenses', rolesLogicExpression: '1', roles: [ - { fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'expense' }, + { + fieldKey: 'root_type', + index: 1, + comparator: 'equals', + value: 'expense', + }, ], columns: DEFAULT_VIEW_COLUMNS, }, diff --git a/packages/webapp/package.json b/packages/webapp/package.json index 7ef65d715..ffccc6209 100644 --- a/packages/webapp/package.json +++ b/packages/webapp/package.json @@ -99,7 +99,7 @@ "yup": "^0.28.1" }, "scripts": { - "dev": "craco start", + "dev": "PORT=4000 craco start", "build": "craco build", "test": "node scripts/test.js", "storybook": "start-storybook -p 6006" diff --git a/packages/webapp/src/style/components/DataTable/DataTable.scss b/packages/webapp/src/style/components/DataTable/DataTable.scss index 1e2e2d347..2dbd1c1af 100644 --- a/packages/webapp/src/style/components/DataTable/DataTable.scss +++ b/packages/webapp/src/style/components/DataTable/DataTable.scss @@ -208,10 +208,9 @@ width: 100%; } } - &:focus { - outline: rgba(0, 82, 204, 0.6) auto 1px; - outline-offset: 1px; + outline: 1px solid rgba(0, 82, 204, 0.7); + outline-offset: -1px; } }