mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
add server to monorepo.
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
import { IAccountTransaction } from '@/interfaces';
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
import { transaction } from 'objection';
|
||||
|
||||
export default class AccountTransactionTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to sale invoice object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'date',
|
||||
'formattedDate',
|
||||
'transactionType',
|
||||
'transactionId',
|
||||
'transactionTypeFormatted',
|
||||
'credit',
|
||||
'debit',
|
||||
'formattedCredit',
|
||||
'formattedDebit',
|
||||
'fcCredit',
|
||||
'fcDebit',
|
||||
'formattedFcCredit',
|
||||
'formattedFcDebit',
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclude all attributes of the model.
|
||||
* @returns {Array<string>}
|
||||
*/
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the formatted date.
|
||||
* @returns {string}
|
||||
*/
|
||||
public formattedDate(transaction: IAccountTransaction) {
|
||||
return this.formatDate(transaction.date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the formatted transaction type.
|
||||
* @returns {string}
|
||||
*/
|
||||
public transactionTypeFormatted(transaction: IAccountTransaction) {
|
||||
return transaction.referenceTypeFormatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the tranasction type.
|
||||
* @returns {string}
|
||||
*/
|
||||
public transactionType(transaction: IAccountTransaction) {
|
||||
return transaction.referenceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the transaction id.
|
||||
* @returns {number}
|
||||
*/
|
||||
public transactionId(transaction: IAccountTransaction) {
|
||||
return transaction.referenceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the credit amount.
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedCredit(transaction: IAccountTransaction) {
|
||||
return this.formatMoney(transaction.credit, {
|
||||
excerptZero: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the credit amount.
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedDebit(transaction: IAccountTransaction) {
|
||||
return this.formatMoney(transaction.debit, {
|
||||
excerptZero: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the foreign credit amount.
|
||||
* @returns {number}
|
||||
*/
|
||||
protected fcCredit(transaction: IAccountTransaction) {
|
||||
return transaction.credit * transaction.exchangeRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the foreign debit amount.
|
||||
* @returns {number}
|
||||
*/
|
||||
protected fcDebit(transaction: IAccountTransaction) {
|
||||
return transaction.debit * transaction.exchangeRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the formatted foreign credit amount.
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedFcCredit(transaction: IAccountTransaction) {
|
||||
return this.formatMoney(this.fcDebit(transaction), {
|
||||
currencyCode: transaction.currencyCode,
|
||||
excerptZero: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the formatted foreign debit amount.
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedFcDebit(transaction: IAccountTransaction) {
|
||||
return this.formatMoney(this.fcCredit(transaction), {
|
||||
currencyCode: transaction.currencyCode,
|
||||
excerptZero: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
24
packages/server/src/services/Accounts/AccountTransform.ts
Normal file
24
packages/server/src/services/Accounts/AccountTransform.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { IAccount } from '@/interfaces';
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
import { formatNumber } from 'utils';
|
||||
|
||||
export class AccountTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to sale invoice object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return ['formattedAmount'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted account amount.
|
||||
* @param {IAccount} invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedAmount = (account: IAccount): string => {
|
||||
return formatNumber(account.amount, {
|
||||
currencyCode: account.currencyCode,
|
||||
});
|
||||
};
|
||||
}
|
||||
139
packages/server/src/services/Accounts/AccountsApplication.ts
Normal file
139
packages/server/src/services/Accounts/AccountsApplication.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import {
|
||||
IAccount,
|
||||
IAccountCreateDTO,
|
||||
IAccountEditDTO,
|
||||
IAccountsFilter,
|
||||
IAccountsTransactionsFilter,
|
||||
IGetAccountTransactionPOJO,
|
||||
} from '@/interfaces';
|
||||
import { CreateAccount } from './CreateAccount';
|
||||
import { DeleteAccount } from './DeleteAccount';
|
||||
import { EditAccount } from './EditAccount';
|
||||
import { ActivateAccount } from './ActivateAccount';
|
||||
import { GetAccounts } from './GetAccounts';
|
||||
import { GetAccount } from './GetAccount';
|
||||
import { GetAccountTransactions } from './GetAccountTransactions';
|
||||
@Service()
|
||||
export class AccountsApplication {
|
||||
@Inject()
|
||||
private createAccountService: CreateAccount;
|
||||
|
||||
@Inject()
|
||||
private deleteAccountService: DeleteAccount;
|
||||
|
||||
@Inject()
|
||||
private editAccountService: EditAccount;
|
||||
|
||||
@Inject()
|
||||
private activateAccountService: ActivateAccount;
|
||||
|
||||
@Inject()
|
||||
private getAccountsService: GetAccounts;
|
||||
|
||||
@Inject()
|
||||
private getAccountService: GetAccount;
|
||||
|
||||
@Inject()
|
||||
private getAccountTransactionsService: GetAccountTransactions;
|
||||
|
||||
/**
|
||||
* Creates a new account.
|
||||
* @param {number} tenantId
|
||||
* @param {IAccountCreateDTO} accountDTO
|
||||
* @returns {Promise<IAccount>}
|
||||
*/
|
||||
public createAccount = (
|
||||
tenantId: number,
|
||||
accountDTO: IAccountCreateDTO
|
||||
): Promise<IAccount> => {
|
||||
return this.createAccountService.createAccount(tenantId, accountDTO);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes the given account.
|
||||
* @param {number} tenantId
|
||||
* @param {number} accountId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public deleteAccount = (tenantId: number, accountId: number) => {
|
||||
return this.deleteAccountService.deleteAccount(tenantId, accountId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Edits the given account.
|
||||
* @param {number} tenantId
|
||||
* @param {number} accountId
|
||||
* @param {IAccountEditDTO} accountDTO
|
||||
* @returns
|
||||
*/
|
||||
public editAccount = (
|
||||
tenantId: number,
|
||||
accountId: number,
|
||||
accountDTO: IAccountEditDTO
|
||||
) => {
|
||||
return this.editAccountService.editAccount(tenantId, accountId, accountDTO);
|
||||
};
|
||||
|
||||
/**
|
||||
* Activate the given account.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} accountId -
|
||||
*/
|
||||
public activateAccount = (tenantId: number, accountId: number) => {
|
||||
return this.activateAccountService.activateAccount(
|
||||
tenantId,
|
||||
accountId,
|
||||
true
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inactivate the given account.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} accountId -
|
||||
*/
|
||||
public inactivateAccount = (tenantId: number, accountId: number) => {
|
||||
return this.activateAccountService.activateAccount(
|
||||
tenantId,
|
||||
accountId,
|
||||
false
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the account details.
|
||||
* @param {number} tenantId
|
||||
* @param {number} accountId
|
||||
* @returns {Promise<IAccount>}
|
||||
*/
|
||||
public getAccount = (tenantId: number, accountId: number) => {
|
||||
return this.getAccountService.getAccount(tenantId, accountId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the accounts list.
|
||||
* @param {number} tenantId
|
||||
* @param {IAccountsFilter} filterDTO
|
||||
* @returns
|
||||
*/
|
||||
public getAccounts = (tenantId: number, filterDTO: IAccountsFilter) => {
|
||||
return this.getAccountsService.getAccountsList(tenantId, filterDTO);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the given account transactions.
|
||||
* @param {number} tenantId
|
||||
* @param {IAccountsTransactionsFilter} filter
|
||||
* @returns {Promise<IGetAccountTransactionPOJO[]>}
|
||||
*/
|
||||
public getAccountsTransactions = (
|
||||
tenantId: number,
|
||||
filter: IAccountsTransactionsFilter
|
||||
): Promise<IGetAccountTransactionPOJO[]> => {
|
||||
return this.getAccountTransactionsService.getAccountsTransactions(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { IAccountsTypesService, IAccountType } from '@/interfaces';
|
||||
import AccountTypesUtils from '@/lib/AccountTypes';
|
||||
import I18nService from '@/services/I18n/I18nService';
|
||||
|
||||
|
||||
@Service()
|
||||
export default class AccountsTypesService implements IAccountsTypesService {
|
||||
@Inject()
|
||||
i18nService: I18nService;
|
||||
|
||||
/**
|
||||
* Retrieve all accounts types.
|
||||
* @param {number} tenantId -
|
||||
* @return {IAccountType}
|
||||
*/
|
||||
public getAccountsTypes(tenantId: number): IAccountType[] {
|
||||
const accountTypes = AccountTypesUtils.getList();
|
||||
return this.i18nService.i18nMapper(accountTypes, ['label'], tenantId);
|
||||
}
|
||||
}
|
||||
64
packages/server/src/services/Accounts/ActivateAccount.ts
Normal file
64
packages/server/src/services/Accounts/ActivateAccount.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { IAccountEventActivatedPayload } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import { CommandAccountValidators } from './CommandAccountValidators';
|
||||
|
||||
@Service()
|
||||
export class ActivateAccount {
|
||||
@Inject()
|
||||
private tenancy: TenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private validator: CommandAccountValidators;
|
||||
|
||||
/**
|
||||
* Activates/Inactivates the given account.
|
||||
* @param {number} tenantId
|
||||
* @param {number} accountId
|
||||
* @param {boolean} activate
|
||||
*/
|
||||
public activateAccount = async (
|
||||
tenantId: number,
|
||||
accountId: number,
|
||||
activate?: boolean
|
||||
) => {
|
||||
const { Account } = this.tenancy.models(tenantId);
|
||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
// Retrieve the given account or throw not found error.
|
||||
const oldAccount = await Account.query()
|
||||
.findById(accountId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Get all children accounts.
|
||||
const accountsGraph = await accountRepository.getDependencyGraph();
|
||||
const dependenciesAccounts = accountsGraph.dependenciesOf(accountId);
|
||||
|
||||
const patchAccountsIds = [...dependenciesAccounts, accountId];
|
||||
|
||||
// Activate account and associated transactions under unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Activate and inactivate the given accounts ids.
|
||||
activate
|
||||
? await accountRepository.activateByIds(patchAccountsIds, trx)
|
||||
: await accountRepository.inactivateByIds(patchAccountsIds, trx);
|
||||
|
||||
// Triggers `onAccountActivated` event.
|
||||
this.eventPublisher.emitAsync(events.accounts.onActivated, {
|
||||
tenantId,
|
||||
accountId,
|
||||
trx,
|
||||
} as IAccountEventActivatedPayload);
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
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 } 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
140
packages/server/src/services/Accounts/CreateAccount.ts
Normal file
140
packages/server/src/services/Accounts/CreateAccount.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { kebabCase } from 'lodash';
|
||||
import { Knex } from 'knex';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import {
|
||||
IAccount,
|
||||
IAccountEventCreatedPayload,
|
||||
IAccountEventCreatingPayload,
|
||||
IAccountCreateDTO,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import { TenantMetadata } from '@/system/models';
|
||||
import { CommandAccountValidators } from './CommandAccountValidators';
|
||||
|
||||
@Service()
|
||||
export class CreateAccount {
|
||||
@Inject()
|
||||
private tenancy: TenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private validator: CommandAccountValidators;
|
||||
|
||||
/**
|
||||
* Authorize the account creation.
|
||||
* @param {number} tenantId
|
||||
* @param {IAccountCreateDTO} accountDTO
|
||||
*/
|
||||
private authorize = async (
|
||||
tenantId: number,
|
||||
accountDTO: IAccountCreateDTO,
|
||||
baseCurrency: string
|
||||
) => {
|
||||
// Validate account name uniquiness.
|
||||
await this.validator.validateAccountNameUniquiness(
|
||||
tenantId,
|
||||
accountDTO.name
|
||||
);
|
||||
// Validate the account code uniquiness.
|
||||
if (accountDTO.code) {
|
||||
await this.validator.isAccountCodeUniqueOrThrowError(
|
||||
tenantId,
|
||||
accountDTO.code
|
||||
);
|
||||
}
|
||||
// Retrieve the account type meta or throw service error if not found.
|
||||
this.validator.getAccountTypeOrThrowError(accountDTO.accountType);
|
||||
|
||||
// Ingore the parent account validation if not presented.
|
||||
if (accountDTO.parentAccountId) {
|
||||
const parentAccount = await this.validator.getParentAccountOrThrowError(
|
||||
tenantId,
|
||||
accountDTO.parentAccountId
|
||||
);
|
||||
this.validator.throwErrorIfParentHasDiffType(accountDTO, parentAccount);
|
||||
|
||||
// Inherit active status from parent account.
|
||||
accountDTO.active = parentAccount.active;
|
||||
|
||||
// Validate should currency code be the same currency of parent account.
|
||||
this.validator.validateCurrentSameParentAccount(
|
||||
accountDTO,
|
||||
parentAccount,
|
||||
baseCurrency
|
||||
);
|
||||
}
|
||||
// Validates the given account type supports the multi-currency.
|
||||
this.validator.validateAccountTypeSupportCurrency(accountDTO, baseCurrency);
|
||||
};
|
||||
|
||||
/**
|
||||
* Transformes the create account DTO to input model.
|
||||
* @param {IAccountCreateDTO} createAccountDTO
|
||||
*/
|
||||
private transformDTOToModel = (
|
||||
createAccountDTO: IAccountCreateDTO,
|
||||
baseCurrency: string
|
||||
) => {
|
||||
return {
|
||||
...createAccountDTO,
|
||||
slug: kebabCase(createAccountDTO.name),
|
||||
currencyCode: createAccountDTO.currencyCode || baseCurrency,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new account on the storage.
|
||||
* @param {number} tenantId
|
||||
* @param {IAccountCreateDTO} accountDTO
|
||||
* @returns {Promise<IAccount>}
|
||||
*/
|
||||
public createAccount = async (
|
||||
tenantId: number,
|
||||
accountDTO: IAccountCreateDTO
|
||||
): Promise<IAccount> => {
|
||||
const { Account } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves the given tenant metadata.
|
||||
const tenantMeta = await TenantMetadata.query().findOne({ tenantId });
|
||||
|
||||
// Authorize the account creation.
|
||||
await this.authorize(tenantId, accountDTO, tenantMeta.baseCurrency);
|
||||
|
||||
// Transformes the DTO to model.
|
||||
const accountInputModel = this.transformDTOToModel(
|
||||
accountDTO,
|
||||
tenantMeta.baseCurrency
|
||||
);
|
||||
// Creates a new account with associated transactions under unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onAccountCreating` event.
|
||||
await this.eventPublisher.emitAsync(events.accounts.onCreating, {
|
||||
tenantId,
|
||||
accountDTO,
|
||||
trx,
|
||||
} as IAccountEventCreatingPayload);
|
||||
|
||||
// Inserts account to the storage.
|
||||
const account = await Account.query(trx).insertAndFetch({
|
||||
...accountInputModel,
|
||||
});
|
||||
// Triggers `onAccountCreated` event.
|
||||
await this.eventPublisher.emitAsync(events.accounts.onCreated, {
|
||||
tenantId,
|
||||
account,
|
||||
accountId: account.id,
|
||||
trx,
|
||||
} as IAccountEventCreatedPayload);
|
||||
|
||||
return account;
|
||||
});
|
||||
};
|
||||
}
|
||||
107
packages/server/src/services/Accounts/DeleteAccount.ts
Normal file
107
packages/server/src/services/Accounts/DeleteAccount.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { IAccountEventDeletedPayload, IAccount } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { CommandAccountValidators } from './CommandAccountValidators';
|
||||
import { ERRORS } from './constants';
|
||||
|
||||
@Service()
|
||||
export class DeleteAccount {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private validator: CommandAccountValidators;
|
||||
|
||||
/**
|
||||
* Authorize account delete.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} accountId - Account id.
|
||||
*/
|
||||
private authorize = async (
|
||||
tenantId: number,
|
||||
accountId: number,
|
||||
oldAccount: IAccount
|
||||
) => {
|
||||
// Throw error if the account was predefined.
|
||||
this.validator.throwErrorIfAccountPredefined(oldAccount);
|
||||
};
|
||||
|
||||
/**
|
||||
* Unlink the given parent account with children accounts.
|
||||
* @param {number} tenantId -
|
||||
* @param {number|number[]} parentAccountId -
|
||||
*/
|
||||
private async unassociateChildrenAccountsFromParent(
|
||||
tenantId: number,
|
||||
parentAccountId: number | number[],
|
||||
trx?: Knex.Transaction
|
||||
) {
|
||||
const { Account } = this.tenancy.models(tenantId);
|
||||
const accountsIds = Array.isArray(parentAccountId)
|
||||
? parentAccountId
|
||||
: [parentAccountId];
|
||||
|
||||
await Account.query(trx)
|
||||
.whereIn('parent_account_id', accountsIds)
|
||||
.patch({ parent_account_id: null });
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the account from the storage.
|
||||
* @param {number} tenantId
|
||||
* @param {number} accountId
|
||||
*/
|
||||
public deleteAccount = async (
|
||||
tenantId: number,
|
||||
accountId: number
|
||||
): Promise<void> => {
|
||||
const { Account } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve account or not found service error.
|
||||
const oldAccount = await Account.query()
|
||||
.findById(accountId)
|
||||
.throwIfNotFound()
|
||||
.queryAndThrowIfHasRelations({
|
||||
type: ERRORS.ACCOUNT_HAS_ASSOCIATED_TRANSACTIONS,
|
||||
});
|
||||
// Authorize before delete account.
|
||||
await this.authorize(tenantId, accountId, oldAccount);
|
||||
|
||||
// Deletes the account and assocaited transactions under UOW envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onAccountDelete` event.
|
||||
await this.eventPublisher.emitAsync(events.accounts.onDelete, {
|
||||
trx,
|
||||
oldAccount,
|
||||
tenantId,
|
||||
} as IAccountEventDeletedPayload);
|
||||
|
||||
// Unlink the parent account from children accounts.
|
||||
await this.unassociateChildrenAccountsFromParent(
|
||||
tenantId,
|
||||
accountId,
|
||||
trx
|
||||
);
|
||||
// Deletes account by the given id.
|
||||
await Account.query(trx).findById(accountId).delete();
|
||||
|
||||
// Triggers `onAccountDeleted` event.
|
||||
await this.eventPublisher.emitAsync(events.accounts.onDeleted, {
|
||||
tenantId,
|
||||
accountId,
|
||||
oldAccount,
|
||||
trx,
|
||||
} as IAccountEventDeletedPayload);
|
||||
});
|
||||
};
|
||||
}
|
||||
116
packages/server/src/services/Accounts/EditAccount.ts
Normal file
116
packages/server/src/services/Accounts/EditAccount.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import {
|
||||
IAccountEventEditedPayload,
|
||||
IAccountEditDTO,
|
||||
IAccount,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import { CommandAccountValidators } from './CommandAccountValidators';
|
||||
|
||||
@Service()
|
||||
export class EditAccount {
|
||||
@Inject()
|
||||
private tenancy: TenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private validator: CommandAccountValidators;
|
||||
|
||||
/**
|
||||
* Authorize the account editing.
|
||||
* @param {number} tenantId
|
||||
* @param {number} accountId
|
||||
* @param {IAccountEditDTO} accountDTO
|
||||
* @param {IAccount} oldAccount -
|
||||
*/
|
||||
private authorize = async (
|
||||
tenantId: number,
|
||||
accountId: number,
|
||||
accountDTO: IAccountEditDTO,
|
||||
oldAccount: IAccount
|
||||
) => {
|
||||
// Validate account name uniquiness.
|
||||
await this.validator.validateAccountNameUniquiness(
|
||||
tenantId,
|
||||
accountDTO.name,
|
||||
accountId
|
||||
);
|
||||
// Validate the account type should be not mutated.
|
||||
await this.validator.isAccountTypeChangedOrThrowError(
|
||||
oldAccount,
|
||||
accountDTO
|
||||
);
|
||||
// Validate the account code not exists on the storage.
|
||||
if (accountDTO.code && accountDTO.code !== oldAccount.code) {
|
||||
await this.validator.isAccountCodeUniqueOrThrowError(
|
||||
tenantId,
|
||||
accountDTO.code,
|
||||
oldAccount.id
|
||||
);
|
||||
}
|
||||
// Retrieve the parent account of throw not found service error.
|
||||
if (accountDTO.parentAccountId) {
|
||||
const parentAccount = await this.validator.getParentAccountOrThrowError(
|
||||
tenantId,
|
||||
accountDTO.parentAccountId,
|
||||
oldAccount.id
|
||||
);
|
||||
this.validator.throwErrorIfParentHasDiffType(accountDTO, parentAccount);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Edits details of the given account.
|
||||
* @param {number} tenantId
|
||||
* @param {number} accountId
|
||||
* @param {IAccountDTO} accountDTO
|
||||
*/
|
||||
public async editAccount(
|
||||
tenantId: number,
|
||||
accountId: number,
|
||||
accountDTO: IAccountEditDTO
|
||||
): Promise<IAccount> {
|
||||
const { Account } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the old account or throw not found service error.
|
||||
const oldAccount = await Account.query()
|
||||
.findById(accountId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Authorize the account editing.
|
||||
await this.authorize(tenantId, accountId, accountDTO, oldAccount);
|
||||
|
||||
// Edits account and associated transactions under unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onAccountEditing` event.
|
||||
await this.eventPublisher.emitAsync(events.accounts.onEditing, {
|
||||
tenantId,
|
||||
oldAccount,
|
||||
accountDTO,
|
||||
});
|
||||
// Update the account on the storage.
|
||||
const account = await Account.query(trx)
|
||||
.findById(accountId)
|
||||
.update({ ...accountDTO });
|
||||
|
||||
// Triggers `onAccountEdited` event.
|
||||
await this.eventPublisher.emitAsync(events.accounts.onEdited, {
|
||||
tenantId,
|
||||
account,
|
||||
oldAccount,
|
||||
trx,
|
||||
} as IAccountEventEditedPayload);
|
||||
|
||||
return account;
|
||||
});
|
||||
}
|
||||
}
|
||||
41
packages/server/src/services/Accounts/GetAccount.ts
Normal file
41
packages/server/src/services/Accounts/GetAccount.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import I18nService from '@/services/I18n/I18nService';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { AccountTransformer } from './AccountTransform';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
|
||||
@Service()
|
||||
export class GetAccount {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private i18nService: I18nService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Retrieve the given account details.
|
||||
* @param {number} tenantId
|
||||
* @param {number} accountId
|
||||
*/
|
||||
public getAccount = async (tenantId: number, accountId: number) => {
|
||||
const { Account } = this.tenancy.models(tenantId);
|
||||
|
||||
// Find the given account or throw not found error.
|
||||
const account = await Account.query().findById(accountId).throwIfNotFound();
|
||||
|
||||
// Transformes the account model to POJO.
|
||||
const transformed = await this.transformer.transform(
|
||||
tenantId,
|
||||
account,
|
||||
new AccountTransformer()
|
||||
);
|
||||
return this.i18nService.i18nApply(
|
||||
[['accountTypeLabel'], ['accountNormalFormatted']],
|
||||
transformed,
|
||||
tenantId
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import {
|
||||
IAccountsTransactionsFilter,
|
||||
IAccountTransaction,
|
||||
IGetAccountTransactionPOJO,
|
||||
} from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
import AccountTransactionTransformer from './AccountTransactionTransformer';
|
||||
|
||||
@Service()
|
||||
export class GetAccountTransactions {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Retrieve the accounts transactions.
|
||||
* @param {number} tenantId -
|
||||
* @param {IAccountsTransactionsFilter} filter -
|
||||
*/
|
||||
public getAccountsTransactions = async (
|
||||
tenantId: number,
|
||||
filter: IAccountsTransactionsFilter
|
||||
): Promise<IGetAccountTransactionPOJO[]> => {
|
||||
const { AccountTransaction, Account } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the given account or throw not found error.
|
||||
if (filter.accountId) {
|
||||
await Account.query().findById(filter.accountId).throwIfNotFound();
|
||||
}
|
||||
const transactions = await AccountTransaction.query().onBuild((query) => {
|
||||
query.orderBy('date', 'DESC');
|
||||
|
||||
if (filter.accountId) {
|
||||
query.where('account_id', filter.accountId);
|
||||
}
|
||||
query.withGraphFetched('account');
|
||||
query.withGraphFetched('contact');
|
||||
query.limit(filter.limit || 50);
|
||||
});
|
||||
// Transform the account transaction.
|
||||
return this.transformer.transform(
|
||||
tenantId,
|
||||
transactions,
|
||||
new AccountTransactionTransformer()
|
||||
);
|
||||
};
|
||||
}
|
||||
66
packages/server/src/services/Accounts/GetAccounts.ts
Normal file
66
packages/server/src/services/Accounts/GetAccounts.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import * as R from 'ramda';
|
||||
import { IAccountsFilter, IAccountResponse, IFilterMeta } from '@/interfaces';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import { AccountTransformer } from './AccountTransform';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
|
||||
@Service()
|
||||
export class GetAccounts {
|
||||
@Inject()
|
||||
private tenancy: TenancyService;
|
||||
|
||||
@Inject()
|
||||
private dynamicListService: DynamicListingService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Parsees accounts list filter DTO.
|
||||
* @param filterDTO
|
||||
* @returns
|
||||
*/
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve accounts datatable list.
|
||||
* @param {number} tenantId
|
||||
* @param {IAccountsFilter} accountsFilter
|
||||
* @returns {Promise<{ accounts: IAccountResponse[]; filterMeta: IFilterMeta }>}
|
||||
*/
|
||||
public getAccountsList = async (
|
||||
tenantId: number,
|
||||
filterDTO: IAccountsFilter
|
||||
): Promise<{ accounts: IAccountResponse[]; filterMeta: IFilterMeta }> => {
|
||||
const { Account } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses the stringified filter roles.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
Account,
|
||||
filter
|
||||
);
|
||||
// Retrieve accounts model based on the given query.
|
||||
const accounts = await Account.query().onBuild((builder) => {
|
||||
dynamicList.buildQuery()(builder);
|
||||
builder.modify('inactiveMode', filter.inactiveMode);
|
||||
});
|
||||
// Retrievs the formatted accounts collection.
|
||||
const transformedAccounts = await this.transformer.transform(
|
||||
tenantId,
|
||||
accounts,
|
||||
new AccountTransformer()
|
||||
);
|
||||
return {
|
||||
accounts: transformedAccounts,
|
||||
filterMeta: dynamicList.getResponseMeta(),
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class MutateBaseCurrencyAccounts {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Mutates the all accounts or the organziation.
|
||||
* @param {number} tenantId
|
||||
* @param {string} currencyCode
|
||||
*/
|
||||
public mutateAllAccountsCurrency = async (
|
||||
tenantId: number,
|
||||
currencyCode: string
|
||||
) => {
|
||||
const { Account } = this.tenancy.models(tenantId);
|
||||
|
||||
await Account.query().update({ currencyCode });
|
||||
};
|
||||
}
|
||||
77
packages/server/src/services/Accounts/constants.ts
Normal file
77
packages/server/src/services/Accounts/constants.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
export const ERRORS = {
|
||||
ACCOUNT_NOT_FOUND: 'account_not_found',
|
||||
ACCOUNT_TYPE_NOT_FOUND: 'account_type_not_found',
|
||||
PARENT_ACCOUNT_NOT_FOUND: 'parent_account_not_found',
|
||||
ACCOUNT_CODE_NOT_UNIQUE: 'account_code_not_unique',
|
||||
ACCOUNT_NAME_NOT_UNIQUE: 'account_name_not_unqiue',
|
||||
PARENT_ACCOUNT_HAS_DIFFERENT_TYPE: 'parent_has_different_type',
|
||||
ACCOUNT_TYPE_NOT_ALLOWED_TO_CHANGE: 'account_type_not_allowed_to_changed',
|
||||
ACCOUNT_PREDEFINED: 'account_predefined',
|
||||
ACCOUNT_HAS_ASSOCIATED_TRANSACTIONS: 'account_has_associated_transactions',
|
||||
PREDEFINED_ACCOUNTS: 'predefined_accounts',
|
||||
ACCOUNTS_HAVE_TRANSACTIONS: 'accounts_have_transactions',
|
||||
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',
|
||||
};
|
||||
|
||||
// Default views columns.
|
||||
export const DEFAULT_VIEW_COLUMNS = [
|
||||
{ key: 'name', label: 'Account name' },
|
||||
{ key: 'code', label: 'Account code' },
|
||||
{ key: 'account_type_label', label: 'Account type' },
|
||||
{ key: 'account_normal', label: 'Account normal' },
|
||||
{ key: 'amount', label: 'Balance' },
|
||||
{ key: 'currencyCode', label: 'Currency' },
|
||||
];
|
||||
|
||||
// Accounts default views.
|
||||
export const DEFAULT_VIEWS = [
|
||||
{
|
||||
name: 'Assets',
|
||||
slug: 'assets',
|
||||
rolesLogicExpression: '1',
|
||||
roles: [
|
||||
{ index: 1, fieldKey: 'root_type', comparator: 'equals', value: 'asset' },
|
||||
],
|
||||
columns: DEFAULT_VIEW_COLUMNS,
|
||||
},
|
||||
{
|
||||
name: 'Liabilities',
|
||||
slug: 'liabilities',
|
||||
rolesLogicExpression: '1',
|
||||
roles: [
|
||||
{ fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'liability' },
|
||||
],
|
||||
columns: DEFAULT_VIEW_COLUMNS,
|
||||
},
|
||||
{
|
||||
name: 'Equity',
|
||||
slug: 'equity',
|
||||
rolesLogicExpression: '1',
|
||||
roles: [
|
||||
{ fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'equity' },
|
||||
],
|
||||
columns: DEFAULT_VIEW_COLUMNS,
|
||||
},
|
||||
{
|
||||
name: 'Income',
|
||||
slug: 'income',
|
||||
rolesLogicExpression: '1',
|
||||
roles: [
|
||||
{ fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'income' },
|
||||
],
|
||||
columns: DEFAULT_VIEW_COLUMNS,
|
||||
},
|
||||
{
|
||||
name: 'Expenses',
|
||||
slug: 'expenses',
|
||||
rolesLogicExpression: '1',
|
||||
roles: [
|
||||
{ fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'expense' },
|
||||
],
|
||||
columns: DEFAULT_VIEW_COLUMNS,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import events from '@/subscribers/events';
|
||||
import { MutateBaseCurrencyAccounts } from '../MutateBaseCurrencyAccounts';
|
||||
|
||||
@Service()
|
||||
export class MutateBaseCurrencyAccountsSubscriber {
|
||||
@Inject()
|
||||
public mutateBaseCurrencyAccounts: MutateBaseCurrencyAccounts;
|
||||
|
||||
/**
|
||||
* Attaches the events with handles.
|
||||
* @param bus
|
||||
*/
|
||||
attach(bus) {
|
||||
bus.subscribe(
|
||||
events.organization.baseCurrencyUpdated,
|
||||
this.updateAccountsCurrencyOnBaseCurrencyMutated
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the all accounts currency once the base currency
|
||||
* of the organization is mutated.
|
||||
*/
|
||||
private updateAccountsCurrencyOnBaseCurrencyMutated = async ({
|
||||
tenantId,
|
||||
organizationDTO,
|
||||
}) => {
|
||||
await this.mutateBaseCurrencyAccounts.mutateAllAccountsCurrency(
|
||||
tenantId,
|
||||
organizationDTO.baseCurrency
|
||||
);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user