mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-14 11:50:31 +00:00
237 lines
6.8 KiB
TypeScript
237 lines
6.8 KiB
TypeScript
import { Inject, Service } from 'typedi';
|
|
import TenancyService from '@/services/Tenancy/TenancyService';
|
|
import { ServiceError } from '@/exceptions';
|
|
import { IAccountDTO, IAccount, IAccountCreateDTO } from '@/interfaces';
|
|
import AccountTypesUtils from '@/lib/AccountTypes';
|
|
import { ERRORS, MAX_ACCOUNTS_CHART_DEPTH } from './constants';
|
|
|
|
@Service()
|
|
export class CommandAccountValidators {
|
|
@Inject()
|
|
private tenancy: TenancyService;
|
|
|
|
/**
|
|
* Throws error if the account was prefined.
|
|
* @param {IAccount} account
|
|
*/
|
|
public throwErrorIfAccountPredefined(account: IAccount) {
|
|
if (account.predefined) {
|
|
throw new ServiceError(ERRORS.ACCOUNT_PREDEFINED);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Diff account type between new and old account, throw service error
|
|
* if they have different account type.
|
|
*
|
|
* @param {IAccount|IAccountDTO} oldAccount
|
|
* @param {IAccount|IAccountDTO} newAccount
|
|
*/
|
|
public async isAccountTypeChangedOrThrowError(
|
|
oldAccount: IAccount | IAccountDTO,
|
|
newAccount: IAccount | IAccountDTO
|
|
) {
|
|
if (oldAccount.accountType !== newAccount.accountType) {
|
|
throw new ServiceError(ERRORS.ACCOUNT_TYPE_NOT_ALLOWED_TO_CHANGE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve account type or throws service error.
|
|
* @param {number} tenantId -
|
|
* @param {number} accountTypeId -
|
|
* @return {IAccountType}
|
|
*/
|
|
public getAccountTypeOrThrowError(accountTypeKey: string) {
|
|
const accountType = AccountTypesUtils.getType(accountTypeKey);
|
|
|
|
if (!accountType) {
|
|
throw new ServiceError(ERRORS.ACCOUNT_TYPE_NOT_FOUND);
|
|
}
|
|
return accountType;
|
|
}
|
|
|
|
/**
|
|
* Retrieve parent account or throw service error.
|
|
* @param {number} tenantId
|
|
* @param {number} accountId
|
|
* @param {number} notAccountId
|
|
*/
|
|
public async getParentAccountOrThrowError(
|
|
tenantId: number,
|
|
accountId: number,
|
|
notAccountId?: number
|
|
) {
|
|
const { Account } = this.tenancy.models(tenantId);
|
|
|
|
const parentAccount = await Account.query()
|
|
.findById(accountId)
|
|
.onBuild((query) => {
|
|
if (notAccountId) {
|
|
query.whereNot('id', notAccountId);
|
|
}
|
|
});
|
|
if (!parentAccount) {
|
|
throw new ServiceError(ERRORS.PARENT_ACCOUNT_NOT_FOUND);
|
|
}
|
|
return parentAccount;
|
|
}
|
|
|
|
/**
|
|
* Throws error if the account type was not unique on the storage.
|
|
* @param {number} tenantId
|
|
* @param {string} accountCode
|
|
* @param {number} notAccountId
|
|
*/
|
|
public async isAccountCodeUniqueOrThrowError(
|
|
tenantId: number,
|
|
accountCode: string,
|
|
notAccountId?: number
|
|
) {
|
|
const { Account } = this.tenancy.models(tenantId);
|
|
|
|
const account = await Account.query()
|
|
.where('code', accountCode)
|
|
.onBuild((query) => {
|
|
if (notAccountId) {
|
|
query.whereNot('id', notAccountId);
|
|
}
|
|
});
|
|
if (account.length > 0) {
|
|
throw new ServiceError(
|
|
ERRORS.ACCOUNT_CODE_NOT_UNIQUE,
|
|
'Account code is not unique.'
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validates the account name uniquiness.
|
|
* @param {number} tenantId
|
|
* @param {string} accountName
|
|
* @param {number} notAccountId - Ignore the account id.
|
|
*/
|
|
public async validateAccountNameUniquiness(
|
|
tenantId: number,
|
|
accountName: string,
|
|
notAccountId?: number
|
|
) {
|
|
const { Account } = this.tenancy.models(tenantId);
|
|
|
|
const foundAccount = await Account.query()
|
|
.findOne('name', accountName)
|
|
.onBuild((query) => {
|
|
if (notAccountId) {
|
|
query.whereNot('id', notAccountId);
|
|
}
|
|
});
|
|
if (foundAccount) {
|
|
throw new ServiceError(
|
|
ERRORS.ACCOUNT_NAME_NOT_UNIQUE,
|
|
'Account name is not unique.'
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validates the given account type supports multi-currency.
|
|
* @param {IAccountDTO} accountDTO -
|
|
*/
|
|
public validateAccountTypeSupportCurrency = (
|
|
accountDTO: IAccountCreateDTO,
|
|
baseCurrency: string
|
|
) => {
|
|
// Can't continue to validate the type has multi-currency feature
|
|
// if the given currency equals the base currency or not assigned.
|
|
if (accountDTO.currencyCode === baseCurrency || !accountDTO.currencyCode) {
|
|
return;
|
|
}
|
|
const meta = AccountTypesUtils.getType(accountDTO.accountType);
|
|
|
|
// Throw error if the account type does not support multi-currency.
|
|
if (!meta?.multiCurrency) {
|
|
throw new ServiceError(ERRORS.ACCOUNT_TYPE_NOT_SUPPORTS_MULTI_CURRENCY);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Validates the account DTO currency code whether equals the currency code of
|
|
* parent account.
|
|
* @param {IAccountCreateDTO} accountDTO
|
|
* @param {IAccount} parentAccount
|
|
* @param {string} baseCurrency -
|
|
* @throws {ServiceError(ERRORS.ACCOUNT_CURRENCY_NOT_SAME_PARENT_ACCOUNT)}
|
|
*/
|
|
public validateCurrentSameParentAccount = (
|
|
accountDTO: IAccountCreateDTO,
|
|
parentAccount: IAccount,
|
|
baseCurrency: string
|
|
) => {
|
|
// If the account DTO currency not assigned and the parent account has no base currency.
|
|
if (
|
|
!accountDTO.currencyCode &&
|
|
parentAccount.currencyCode !== baseCurrency
|
|
) {
|
|
throw new ServiceError(ERRORS.ACCOUNT_CURRENCY_NOT_SAME_PARENT_ACCOUNT);
|
|
}
|
|
// If the account DTO is assigned and not equals the currency code of parent account.
|
|
if (
|
|
accountDTO.currencyCode &&
|
|
parentAccount.currencyCode !== accountDTO.currencyCode
|
|
) {
|
|
throw new ServiceError(ERRORS.ACCOUNT_CURRENCY_NOT_SAME_PARENT_ACCOUNT);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Throws service error if parent account has different type.
|
|
* @param {IAccountDTO} accountDTO
|
|
* @param {IAccount} parentAccount
|
|
*/
|
|
public throwErrorIfParentHasDiffType(
|
|
accountDTO: IAccountDTO,
|
|
parentAccount: IAccount
|
|
) {
|
|
if (accountDTO.accountType !== parentAccount.accountType) {
|
|
throw new ServiceError(ERRORS.PARENT_ACCOUNT_HAS_DIFFERENT_TYPE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve account of throw service error in case account not found.
|
|
* @param {number} tenantId
|
|
* @param {number} accountId
|
|
* @return {IAccount}
|
|
*/
|
|
public async getAccountOrThrowError(tenantId: number, accountId: number) {
|
|
const { accountRepository } = this.tenancy.repositories(tenantId);
|
|
|
|
const account = await accountRepository.findOneById(accountId);
|
|
|
|
if (!account) {
|
|
throw new ServiceError(ERRORS.ACCOUNT_NOT_FOUND);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|