mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 21:00:31 +00:00
700 lines
20 KiB
TypeScript
700 lines
20 KiB
TypeScript
import { Inject, Service } from 'typedi';
|
|
import { difference, chain, uniq } from 'lodash';
|
|
import { kebabCase } from 'lodash';
|
|
import TenancyService from 'services/Tenancy/TenancyService';
|
|
import { ServiceError } from 'exceptions';
|
|
import {
|
|
IAccountDTO,
|
|
IAccount,
|
|
IAccountsFilter,
|
|
IFilterMeta,
|
|
IAccountResponse
|
|
} from 'interfaces';
|
|
import {
|
|
EventDispatcher,
|
|
EventDispatcherInterface,
|
|
} from 'decorators/eventDispatcher';
|
|
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
|
import events from 'subscribers/events';
|
|
import AccountTypesUtils from 'lib/AccountTypes';
|
|
import { ERRORS } from './constants';
|
|
|
|
@Service()
|
|
export default class AccountsService {
|
|
@Inject()
|
|
tenancy: TenancyService;
|
|
|
|
@Inject()
|
|
dynamicListService: DynamicListingService;
|
|
|
|
@Inject('logger')
|
|
logger: any;
|
|
|
|
@EventDispatcher()
|
|
eventDispatcher: EventDispatcherInterface;
|
|
|
|
/**
|
|
* Retrieve account type or throws service error.
|
|
* @param {number} tenantId -
|
|
* @param {number} accountTypeId -
|
|
* @return {IAccountType}
|
|
*/
|
|
private getAccountTypeOrThrowError(accountTypeKey: string) {
|
|
this.logger.info('[accounts] validating account type existance.', {
|
|
accountTypeKey,
|
|
});
|
|
const accountType = AccountTypesUtils.getType(accountTypeKey);
|
|
|
|
if (!accountType) {
|
|
this.logger.info('[accounts] account type not found.');
|
|
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
|
|
*/
|
|
private async getParentAccountOrThrowError(
|
|
tenantId: number,
|
|
accountId: number,
|
|
notAccountId?: number
|
|
) {
|
|
const { Account } = this.tenancy.models(tenantId);
|
|
|
|
this.logger.info('[accounts] validating parent account existance.', {
|
|
tenantId,
|
|
accountId,
|
|
notAccountId,
|
|
});
|
|
const parentAccount = await Account.query()
|
|
.findById(accountId)
|
|
.onBuild((query) => {
|
|
if (notAccountId) {
|
|
query.whereNot('id', notAccountId);
|
|
}
|
|
});
|
|
if (!parentAccount) {
|
|
this.logger.info('[accounts] parent account not found.', {
|
|
tenantId,
|
|
accountId,
|
|
});
|
|
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
|
|
*/
|
|
private async isAccountCodeUniqueOrThrowError(
|
|
tenantId: number,
|
|
accountCode: string,
|
|
notAccountId?: number
|
|
) {
|
|
const { Account } = this.tenancy.models(tenantId);
|
|
|
|
this.logger.info(
|
|
'[accounts] validating the account code unique on the storage.',
|
|
{
|
|
tenantId,
|
|
accountCode,
|
|
notAccountId,
|
|
}
|
|
);
|
|
const account = await Account.query()
|
|
.where('code', accountCode)
|
|
.onBuild((query) => {
|
|
if (notAccountId) {
|
|
query.whereNot('id', notAccountId);
|
|
}
|
|
});
|
|
|
|
if (account.length > 0) {
|
|
this.logger.info('[accounts] account code is not unique.', {
|
|
tenantId,
|
|
accountCode,
|
|
});
|
|
throw new ServiceError(ERRORS.ACCOUNT_CODE_NOT_UNIQUE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Throws service error if parent account has different type.
|
|
* @param {IAccountDTO} accountDTO
|
|
* @param {IAccount} parentAccount
|
|
*/
|
|
private 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);
|
|
|
|
this.logger.info('[accounts] validating the account existance.', {
|
|
tenantId,
|
|
accountId,
|
|
});
|
|
const account = await accountRepository.findOneById(accountId);
|
|
|
|
if (!account) {
|
|
this.logger.info('[accounts] the given account not found.', {
|
|
accountId,
|
|
});
|
|
throw new ServiceError(ERRORS.ACCOUNT_NOT_FOUND);
|
|
}
|
|
return account;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
private async isAccountTypeChangedOrThrowError(
|
|
oldAccount: IAccount | IAccountDTO,
|
|
newAccount: IAccount | IAccountDTO
|
|
) {
|
|
if (oldAccount.accountType !== newAccount.accountType) {
|
|
throw new ServiceError(ERRORS.ACCOUNT_TYPE_NOT_ALLOWED_TO_CHANGE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validates the account name uniquiness.
|
|
* @param {number} tenantId
|
|
* @param {string} accountName
|
|
* @param {number} notAccountId - Ignore the account id.
|
|
*/
|
|
private async validateAccountNameUniquiness(
|
|
tenantId: number,
|
|
accountName: string,
|
|
notAccountId?: number
|
|
) {
|
|
const { Account } = this.tenancy.models(tenantId);
|
|
|
|
this.logger.info('[accounts] validating account name uniquiness.', {
|
|
tenantId,
|
|
accountName,
|
|
notAccountId,
|
|
});
|
|
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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a new account on the storage.
|
|
* @param {number} tenantId
|
|
* @param {IAccount} accountDTO
|
|
* @returns {IAccount}
|
|
*/
|
|
public async newAccount(tenantId: number, accountDTO: IAccountDTO) {
|
|
const { accountRepository } = this.tenancy.repositories(tenantId);
|
|
|
|
// Validate account name uniquiness.
|
|
await this.validateAccountNameUniquiness(tenantId, accountDTO.name);
|
|
|
|
// Validate the account code uniquiness.
|
|
if (accountDTO.code) {
|
|
await this.isAccountCodeUniqueOrThrowError(tenantId, accountDTO.code);
|
|
}
|
|
this.getAccountTypeOrThrowError(accountDTO.accountType);
|
|
|
|
if (accountDTO.parentAccountId) {
|
|
const parentAccount = await this.getParentAccountOrThrowError(
|
|
tenantId,
|
|
accountDTO.parentAccountId
|
|
);
|
|
this.throwErrorIfParentHasDiffType(accountDTO, parentAccount);
|
|
|
|
// Inherit active status from parent account.
|
|
accountDTO.active = parentAccount.active;
|
|
}
|
|
const account = await accountRepository.create({
|
|
...accountDTO,
|
|
slug: kebabCase(accountDTO.name),
|
|
});
|
|
this.logger.info('[account] account created successfully.', {
|
|
account,
|
|
accountDTO,
|
|
});
|
|
|
|
// Triggers `onAccountCreated` event.
|
|
this.eventDispatcher.dispatch(events.accounts.onCreated);
|
|
|
|
return account;
|
|
}
|
|
|
|
/**
|
|
* Edits details of the given account.
|
|
* @param {number} tenantId
|
|
* @param {number} accountId
|
|
* @param {IAccountDTO} accountDTO
|
|
*/
|
|
public async editAccount(
|
|
tenantId: number,
|
|
accountId: number,
|
|
accountDTO: IAccountDTO
|
|
) {
|
|
this.logger.info('[account] trying to edit account.', {
|
|
tenantId,
|
|
accountId,
|
|
});
|
|
const { accountRepository } = this.tenancy.repositories(tenantId);
|
|
const oldAccount = await this.getAccountOrThrowError(tenantId, accountId);
|
|
|
|
// Validate account name uniquiness.
|
|
await this.validateAccountNameUniquiness(
|
|
tenantId,
|
|
accountDTO.name,
|
|
accountId
|
|
);
|
|
|
|
await this.isAccountTypeChangedOrThrowError(oldAccount, accountDTO);
|
|
|
|
// Validate the account code not exists on the storage.
|
|
if (accountDTO.code && accountDTO.code !== oldAccount.code) {
|
|
await this.isAccountCodeUniqueOrThrowError(
|
|
tenantId,
|
|
accountDTO.code,
|
|
oldAccount.id
|
|
);
|
|
}
|
|
if (accountDTO.parentAccountId) {
|
|
const parentAccount = await this.getParentAccountOrThrowError(
|
|
tenantId,
|
|
accountDTO.parentAccountId,
|
|
oldAccount.id
|
|
);
|
|
this.throwErrorIfParentHasDiffType(accountDTO, parentAccount);
|
|
}
|
|
// Update the account on the storage.
|
|
const account = await accountRepository.update(
|
|
{ ...accountDTO },
|
|
{ id: oldAccount.id }
|
|
);
|
|
this.logger.info('[account] account edited successfully.', {
|
|
account,
|
|
accountDTO,
|
|
tenantId,
|
|
});
|
|
// Triggers `onAccountEdited` event.
|
|
this.eventDispatcher.dispatch(events.accounts.onEdited);
|
|
|
|
return account;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the given account details.
|
|
* @param {number} tenantId
|
|
* @param {number} accountId
|
|
*/
|
|
public async getAccount(tenantId: number, accountId: number) {
|
|
return this.getAccountOrThrowError(tenantId, accountId);
|
|
}
|
|
|
|
/**
|
|
* Detarmine if the given account id exists on the storage.
|
|
* @param {number} tenantId
|
|
* @param {number} accountId
|
|
*/
|
|
public async isAccountExists(tenantId: number, accountId: number) {
|
|
const { Account } = this.tenancy.models(tenantId);
|
|
|
|
this.logger.info('[account] validating the account existance.', {
|
|
tenantId,
|
|
accountId,
|
|
});
|
|
const foundAccounts = await Account.query().where('id', accountId);
|
|
|
|
return foundAccounts.length > 0;
|
|
}
|
|
|
|
/**
|
|
* Throws error if the account was prefined.
|
|
* @param {IAccount} account
|
|
*/
|
|
private throwErrorIfAccountPredefined(account: IAccount) {
|
|
if (account.predefined) {
|
|
throw new ServiceError(ERRORS.ACCOUNT_PREDEFINED);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unlink the given parent account with children accounts.
|
|
* @param {number} tenantId -
|
|
* @param {number|number[]} parentAccountId -
|
|
*/
|
|
private async unassociateChildrenAccountsFromParent(
|
|
tenantId: number,
|
|
parentAccountId: number | number[]
|
|
) {
|
|
const { Account } = this.tenancy.models(tenantId);
|
|
const accountsIds = Array.isArray(parentAccountId)
|
|
? parentAccountId
|
|
: [parentAccountId];
|
|
|
|
await Account.query()
|
|
.whereIn('parent_account_id', accountsIds)
|
|
.patch({ parent_account_id: null });
|
|
}
|
|
|
|
/**
|
|
* Throws service error if the account has associated transactions.
|
|
* @param {number} tenantId
|
|
* @param {number} accountId
|
|
*/
|
|
private async throwErrorIfAccountHasTransactions(
|
|
tenantId: number,
|
|
accountId: number
|
|
) {
|
|
const { AccountTransaction } = this.tenancy.models(tenantId);
|
|
const accountTransactions = await AccountTransaction.query().where(
|
|
'account_id',
|
|
accountId
|
|
);
|
|
if (accountTransactions.length > 0) {
|
|
throw new ServiceError(ERRORS.ACCOUNT_HAS_ASSOCIATED_TRANSACTIONS);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes the account from the storage.
|
|
* @param {number} tenantId
|
|
* @param {number} accountId
|
|
*/
|
|
public async deleteAccount(tenantId: number, accountId: number) {
|
|
const { accountRepository } = this.tenancy.repositories(tenantId);
|
|
const account = await this.getAccountOrThrowError(tenantId, accountId);
|
|
|
|
// Throw error if the account was predefined.
|
|
this.throwErrorIfAccountPredefined(account);
|
|
|
|
// Throw error if the account has transactions.
|
|
await this.throwErrorIfAccountHasTransactions(tenantId, accountId);
|
|
|
|
// Unlink the parent account from children accounts.
|
|
await this.unassociateChildrenAccountsFromParent(tenantId, accountId);
|
|
|
|
await accountRepository.deleteById(account.id);
|
|
this.logger.info('[account] account has been deleted successfully.', {
|
|
tenantId,
|
|
accountId,
|
|
});
|
|
|
|
// Triggers `onAccountDeleted` event.
|
|
this.eventDispatcher.dispatch(events.accounts.onDeleted);
|
|
}
|
|
|
|
/**
|
|
* Retrieve the given accounts details or throw error if one account not exists.
|
|
* @param {number} tenantId
|
|
* @param {number[]} accountsIds
|
|
* @return {IAccount[]}
|
|
*/
|
|
public async getAccountsOrThrowError(
|
|
tenantId: number,
|
|
accountsIds: number[]
|
|
): Promise<IAccount[]> {
|
|
const { Account } = this.tenancy.models(tenantId);
|
|
|
|
this.logger.info('[account] trying to validate accounts not exist.', {
|
|
tenantId,
|
|
accountsIds,
|
|
});
|
|
const storedAccounts = await Account.query().whereIn('id', accountsIds);
|
|
const storedAccountsIds = storedAccounts.map((account) => account.id);
|
|
const notFoundAccounts = difference(accountsIds, storedAccountsIds);
|
|
|
|
if (notFoundAccounts.length > 0) {
|
|
this.logger.error('[account] accounts not exists on the storage.', {
|
|
tenantId,
|
|
notFoundAccounts,
|
|
});
|
|
throw new ServiceError(ERRORS.ACCOUNTS_NOT_FOUND);
|
|
}
|
|
return storedAccounts;
|
|
}
|
|
|
|
/**
|
|
* Validate whether one of the given accounts is predefined.
|
|
* @param {IAccount[]} accounts -
|
|
* @return {IAccount[]} - Predefined accounts
|
|
*/
|
|
private validatePrefinedAccounts(accounts: IAccount[]) {
|
|
const predefined = accounts.filter(
|
|
(account: IAccount) => account.predefined
|
|
);
|
|
|
|
if (predefined.length > 0) {
|
|
this.logger.error('[accounts] some accounts predefined.', { predefined });
|
|
throw new ServiceError(ERRORS.PREDEFINED_ACCOUNTS);
|
|
}
|
|
return predefined;
|
|
}
|
|
|
|
/**
|
|
* Validating the accounts have associated transactions.
|
|
* @param {number} tenantId
|
|
* @param {number[]} accountsIds
|
|
*/
|
|
private async validateAccountsHaveTransactions(
|
|
tenantId: number,
|
|
accountsIds: number[]
|
|
) {
|
|
const { AccountTransaction } = this.tenancy.models(tenantId);
|
|
const accountsTransactions = await AccountTransaction.query()
|
|
.whereIn('account_id', accountsIds)
|
|
.count('id as transactions_count')
|
|
.groupBy('account_id')
|
|
.select('account_id');
|
|
|
|
const accountsHasTransactions: number[] = [];
|
|
|
|
accountsTransactions.forEach((transaction) => {
|
|
if (transaction.transactionsCount > 0) {
|
|
accountsHasTransactions.push(transaction.accountId);
|
|
}
|
|
});
|
|
if (accountsHasTransactions.length > 0) {
|
|
throw new ServiceError(ERRORS.ACCOUNTS_HAVE_TRANSACTIONS);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes the given accounts in bulk.
|
|
* @param {number} tenantId
|
|
* @param {number[]} accountsIds
|
|
*/
|
|
public async deleteAccounts(tenantId: number, accountsIds: number[]) {
|
|
const { accountRepository } = this.tenancy.models(tenantId);
|
|
const accounts = await this.getAccountsOrThrowError(tenantId, accountsIds);
|
|
|
|
// Validate the accounts are not predefined.
|
|
this.validatePrefinedAccounts(accounts);
|
|
|
|
// Valdiate the accounts have transactions.
|
|
await this.validateAccountsHaveTransactions(tenantId, accountsIds);
|
|
|
|
// Unlink the parent account from children accounts.
|
|
await this.unassociateChildrenAccountsFromParent(tenantId, accountsIds);
|
|
|
|
// Delete the accounts in one query.
|
|
await accountRepository.deleteWhereIdIn(accountsIds);
|
|
|
|
this.logger.info('[account] given accounts deleted in bulk successfully.', {
|
|
tenantId,
|
|
accountsIds,
|
|
});
|
|
// Triggers `onBulkDeleted` event.
|
|
this.eventDispatcher.dispatch(events.accounts.onBulkDeleted);
|
|
}
|
|
|
|
/**
|
|
* Activate accounts in bulk.
|
|
* @param {number} tenantId
|
|
* @param {number[]} accountsIds
|
|
* @param {boolean} activate
|
|
*/
|
|
public async activateAccounts(
|
|
tenantId: number,
|
|
accountsIds: number[],
|
|
activate: boolean = true
|
|
) {
|
|
const { accountRepository } = this.tenancy.repositories(tenantId);
|
|
|
|
// Retrieve the given account or throw not found.
|
|
await this.getAccountsOrThrowError(tenantId, accountsIds);
|
|
|
|
// Get all children accounts.
|
|
const accountsGraph = await accountRepository.getDependencyGraph();
|
|
const dependenciesAccounts = chain(accountsIds)
|
|
.map((accountId) => accountsGraph.dependenciesOf(accountId))
|
|
.flatten()
|
|
.value();
|
|
|
|
// The children and parent accounts.
|
|
const patchAccountsIds = uniq([...dependenciesAccounts, accountsIds]);
|
|
|
|
this.logger.info(
|
|
'[account] trying activate/inactive the given accounts ids.',
|
|
{ accountsIds }
|
|
);
|
|
// Activate or inactivate the given accounts ids in bulk.
|
|
activate
|
|
? await accountRepository.activateByIds(patchAccountsIds)
|
|
: await accountRepository.inactivateByIds(patchAccountsIds);
|
|
|
|
this.logger.info('[account] accounts have been activated successfully.', {
|
|
tenantId,
|
|
accountsIds,
|
|
});
|
|
// Triggers `onAccountBulkActivated` event.
|
|
this.eventDispatcher.dispatch(events.accounts.onActivated);
|
|
}
|
|
|
|
/**
|
|
* Activates/Inactivates the given account.
|
|
* @param {number} tenantId
|
|
* @param {number} accountId
|
|
* @param {boolean} activate
|
|
*/
|
|
public async activateAccount(
|
|
tenantId: number,
|
|
accountId: number,
|
|
activate?: boolean
|
|
) {
|
|
const { accountRepository } = this.tenancy.repositories(tenantId);
|
|
|
|
// Retrieve the given account or throw not found error.
|
|
const account = await this.getAccountOrThrowError(tenantId, accountId);
|
|
|
|
// Get all children accounts.
|
|
const accountsGraph = await accountRepository.getDependencyGraph();
|
|
const dependenciesAccounts = accountsGraph.dependenciesOf(accountId);
|
|
|
|
this.logger.info(
|
|
'[account] trying to activate/inactivate the given account id.'
|
|
);
|
|
const patchAccountsIds = [...dependenciesAccounts, accountId];
|
|
|
|
// Activate and inactivate the given accounts ids.
|
|
activate
|
|
? await accountRepository.activateByIds(patchAccountsIds)
|
|
: await accountRepository.inactivateByIds(patchAccountsIds);
|
|
|
|
this.logger.info('[account] account have been activated successfully.', {
|
|
tenantId,
|
|
accountId,
|
|
});
|
|
// Triggers `onAccountActivated` event.
|
|
this.eventDispatcher.dispatch(events.accounts.onActivated);
|
|
}
|
|
|
|
/**
|
|
* Retrieve accounts datatable list.
|
|
* @param {number} tenantId
|
|
* @param {IAccountsFilter} accountsFilter
|
|
*/
|
|
public async getAccountsList(
|
|
tenantId: number,
|
|
filter: IAccountsFilter
|
|
): Promise<{ accounts: IAccountResponse[]; filterMeta: IFilterMeta }> {
|
|
const { Account } = this.tenancy.models(tenantId);
|
|
const dynamicList = await this.dynamicListService.dynamicList(
|
|
tenantId,
|
|
Account,
|
|
filter
|
|
);
|
|
|
|
this.logger.info('[accounts] trying to get accounts datatable list.', {
|
|
tenantId,
|
|
filter,
|
|
});
|
|
const accounts = await Account.query().onBuild((builder) => {
|
|
dynamicList.buildQuery()(builder);
|
|
});
|
|
|
|
return {
|
|
accounts: this.transformAccountsResponse(tenantId, accounts),
|
|
filterMeta: dynamicList.getResponseMeta(),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Transformes the accounts models to accounts response.
|
|
*/
|
|
private transformAccountsResponse(tenantId: number, accounts: IAccount[]) {
|
|
const settings = this.tenancy.settings(tenantId);
|
|
const baseCurrency = settings.get({
|
|
group: 'organization',
|
|
key: 'base_currency',
|
|
});
|
|
|
|
return accounts.map((account) => ({
|
|
...account.toJSON(),
|
|
currencyCode: baseCurrency,
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Closes the given account.
|
|
* -----------
|
|
* Precedures.
|
|
* -----------
|
|
* - Transfer the given account transactions to another account with the same root type.
|
|
* - Delete the given account.
|
|
* -------
|
|
* @param {number} tenantId -
|
|
* @param {number} accountId -
|
|
* @param {number} toAccountId -
|
|
* @param {boolean} deleteAfterClosing -
|
|
*/
|
|
public async closeAccount(
|
|
tenantId: number,
|
|
accountId: number,
|
|
toAccountId: number,
|
|
deleteAfterClosing: boolean
|
|
) {
|
|
this.logger.info('[account] trying to close account.', {
|
|
tenantId,
|
|
accountId,
|
|
toAccountId,
|
|
deleteAfterClosing,
|
|
});
|
|
const { AccountTransaction } = this.tenancy.models(tenantId);
|
|
const { accountRepository } = this.tenancy.repositories(tenantId);
|
|
|
|
const account = await this.getAccountOrThrowError(tenantId, accountId);
|
|
const toAccount = await this.getAccountOrThrowError(tenantId, toAccountId);
|
|
|
|
this.throwErrorIfAccountPredefined(account);
|
|
|
|
if (account.accountType !== toAccount.accountType) {
|
|
throw new ServiceError(ERRORS.CLOSE_ACCOUNT_AND_TO_ACCOUNT_NOT_SAME_TYPE);
|
|
}
|
|
const updateAccountBalanceOper = await accountRepository.balanceChange(
|
|
accountId,
|
|
account.balance || 0
|
|
);
|
|
|
|
// Move transactiosn operation.
|
|
const moveTransactionsOper = await AccountTransaction.query()
|
|
.where('account_id', accountId)
|
|
.patch({ accountId: toAccountId });
|
|
|
|
await Promise.all([moveTransactionsOper, updateAccountBalanceOper]);
|
|
|
|
if (deleteAfterClosing) {
|
|
await accountRepository.deleteById(accountId);
|
|
}
|
|
}
|
|
}
|