mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-26 17:49:48 +00:00
Merge pull request #994 from bigcapitalhq/feat/account-settings-service
fix(accounts): add account settings service
This commit is contained in:
@@ -21,6 +21,7 @@ import { AccountsExportable } from './AccountsExportable.service';
|
|||||||
import { AccountsImportable } from './AccountsImportable.service';
|
import { AccountsImportable } from './AccountsImportable.service';
|
||||||
import { BulkDeleteAccountsService } from './BulkDeleteAccounts.service';
|
import { BulkDeleteAccountsService } from './BulkDeleteAccounts.service';
|
||||||
import { ValidateBulkDeleteAccountsService } from './ValidateBulkDeleteAccounts.service';
|
import { ValidateBulkDeleteAccountsService } from './ValidateBulkDeleteAccounts.service';
|
||||||
|
import { AccountsSettingsService } from './AccountsSettings.service';
|
||||||
|
|
||||||
const models = [RegisterTenancyModel(BankAccount)];
|
const models = [RegisterTenancyModel(BankAccount)];
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ const models = [RegisterTenancyModel(BankAccount)];
|
|||||||
controllers: [AccountsController],
|
controllers: [AccountsController],
|
||||||
providers: [
|
providers: [
|
||||||
AccountsApplication,
|
AccountsApplication,
|
||||||
|
AccountsSettingsService,
|
||||||
CreateAccountService,
|
CreateAccountService,
|
||||||
TenancyContext,
|
TenancyContext,
|
||||||
CommandAccountValidators,
|
CommandAccountValidators,
|
||||||
@@ -49,9 +51,10 @@ const models = [RegisterTenancyModel(BankAccount)];
|
|||||||
exports: [
|
exports: [
|
||||||
AccountRepository,
|
AccountRepository,
|
||||||
CreateAccountService,
|
CreateAccountService,
|
||||||
|
AccountsSettingsService,
|
||||||
...models,
|
...models,
|
||||||
AccountsExportable,
|
AccountsExportable,
|
||||||
AccountsImportable
|
AccountsImportable,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AccountsModule {}
|
export class AccountsModule {}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { SettingsStore } from '../Settings/SettingsStore';
|
||||||
|
import { SETTINGS_PROVIDER } from '../Settings/Settings.types';
|
||||||
|
|
||||||
|
export interface IAccountsSettings {
|
||||||
|
accountCodeRequired: boolean;
|
||||||
|
accountCodeUnique: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AccountsSettingsService {
|
||||||
|
constructor(
|
||||||
|
@Inject(SETTINGS_PROVIDER)
|
||||||
|
private readonly settingsStore: () => SettingsStore,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves account settings (account code required, account code unique).
|
||||||
|
*/
|
||||||
|
public async getAccountsSettings(): Promise<IAccountsSettings> {
|
||||||
|
const settingsStore = await this.settingsStore();
|
||||||
|
return {
|
||||||
|
accountCodeRequired: settingsStore.get(
|
||||||
|
{ group: 'accounts', key: 'account_code_required' },
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
accountCodeUnique: settingsStore.get(
|
||||||
|
{ group: 'accounts', key: 'account_code_unique' },
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -106,6 +106,20 @@ export class CommandAccountValidators {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws error if account code is missing or blank when required.
|
||||||
|
* @param {string|undefined} code - Account code.
|
||||||
|
*/
|
||||||
|
public validateAccountCodeRequiredOrThrow(code: string | undefined) {
|
||||||
|
const trimmed = typeof code === 'string' ? code.trim() : '';
|
||||||
|
if (!trimmed) {
|
||||||
|
throw new ServiceError(
|
||||||
|
ERRORS.ACCOUNT_CODE_REQUIRED,
|
||||||
|
'Account code is required.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the account name uniquiness.
|
* Validates the account name uniquiness.
|
||||||
* @param {string} accountName - Account name.
|
* @param {string} accountName - Account name.
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { events } from '@/common/events/events';
|
|||||||
import { CreateAccountDTO } from './CreateAccount.dto';
|
import { CreateAccountDTO } from './CreateAccount.dto';
|
||||||
import { PartialModelObject } from 'objection';
|
import { PartialModelObject } from 'objection';
|
||||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||||
|
import { AccountsSettingsService } from './AccountsSettings.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CreateAccountService {
|
export class CreateAccountService {
|
||||||
@@ -32,6 +33,7 @@ export class CreateAccountService {
|
|||||||
private readonly uow: UnitOfWork,
|
private readonly uow: UnitOfWork,
|
||||||
private readonly validator: CommandAccountValidators,
|
private readonly validator: CommandAccountValidators,
|
||||||
private readonly tenancyContext: TenancyContext,
|
private readonly tenancyContext: TenancyContext,
|
||||||
|
private readonly accountsSettings: AccountsSettingsService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,14 +45,21 @@ export class CreateAccountService {
|
|||||||
baseCurrency: string,
|
baseCurrency: string,
|
||||||
params?: CreateAccountParams,
|
params?: CreateAccountParams,
|
||||||
) => {
|
) => {
|
||||||
|
const { accountCodeRequired, accountCodeUnique } =
|
||||||
|
await this.accountsSettings.getAccountsSettings();
|
||||||
|
|
||||||
|
// Validate account code required when setting is enabled.
|
||||||
|
if (accountCodeRequired) {
|
||||||
|
this.validator.validateAccountCodeRequiredOrThrow(accountDTO.code);
|
||||||
|
}
|
||||||
|
// Validate the account code uniquiness when setting is enabled.
|
||||||
|
if (accountCodeUnique && accountDTO.code?.trim()) {
|
||||||
|
await this.validator.isAccountCodeUniqueOrThrowError(accountDTO.code);
|
||||||
|
}
|
||||||
// Validate account name uniquiness.
|
// Validate account name uniquiness.
|
||||||
if (!params.ignoreUniqueName) {
|
if (!params.ignoreUniqueName) {
|
||||||
await this.validator.validateAccountNameUniquiness(accountDTO.name);
|
await this.validator.validateAccountNameUniquiness(accountDTO.name);
|
||||||
}
|
}
|
||||||
// Validate the account code uniquiness.
|
|
||||||
if (accountDTO.code) {
|
|
||||||
await this.validator.isAccountCodeUniqueOrThrowError(accountDTO.code);
|
|
||||||
}
|
|
||||||
// Retrieve the account type meta or throw service error if not found.
|
// Retrieve the account type meta or throw service error if not found.
|
||||||
this.validator.getAccountTypeOrThrowError(accountDTO.accountType);
|
this.validator.getAccountTypeOrThrowError(accountDTO.accountType);
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { UnitOfWork } from '../Tenancy/TenancyDB/UnitOfWork.service';
|
|||||||
import { events } from '@/common/events/events';
|
import { events } from '@/common/events/events';
|
||||||
import { EditAccountDTO } from './EditAccount.dto';
|
import { EditAccountDTO } from './EditAccount.dto';
|
||||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||||
|
import { AccountsSettingsService } from './AccountsSettings.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class EditAccount {
|
export class EditAccount {
|
||||||
@@ -17,7 +18,8 @@ export class EditAccount {
|
|||||||
|
|
||||||
@Inject(Account.name)
|
@Inject(Account.name)
|
||||||
private readonly accountModel: TenantModelProxy<typeof Account>,
|
private readonly accountModel: TenantModelProxy<typeof Account>,
|
||||||
) { }
|
private readonly accountsSettings: AccountsSettingsService,
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authorize the account editing.
|
* Authorize the account editing.
|
||||||
@@ -30,6 +32,24 @@ export class EditAccount {
|
|||||||
accountDTO: EditAccountDTO,
|
accountDTO: EditAccountDTO,
|
||||||
oldAccount: Account,
|
oldAccount: Account,
|
||||||
) => {
|
) => {
|
||||||
|
const { accountCodeRequired, accountCodeUnique } =
|
||||||
|
await this.accountsSettings.getAccountsSettings();
|
||||||
|
|
||||||
|
// Validate account code required when setting is enabled.
|
||||||
|
if (accountCodeRequired) {
|
||||||
|
this.validator.validateAccountCodeRequiredOrThrow(accountDTO.code);
|
||||||
|
}
|
||||||
|
// Validate the account code uniquiness when setting is enabled.
|
||||||
|
if (
|
||||||
|
accountCodeUnique &&
|
||||||
|
accountDTO.code?.trim() &&
|
||||||
|
accountDTO.code !== oldAccount.code
|
||||||
|
) {
|
||||||
|
await this.validator.isAccountCodeUniqueOrThrowError(
|
||||||
|
accountDTO.code,
|
||||||
|
oldAccount.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
// Validate account name uniquiness.
|
// Validate account name uniquiness.
|
||||||
await this.validator.validateAccountNameUniquiness(
|
await this.validator.validateAccountNameUniquiness(
|
||||||
accountDTO.name,
|
accountDTO.name,
|
||||||
@@ -40,13 +60,6 @@ export class EditAccount {
|
|||||||
oldAccount,
|
oldAccount,
|
||||||
accountDTO,
|
accountDTO,
|
||||||
);
|
);
|
||||||
// Validate the account code not exists on the storage.
|
|
||||||
if (accountDTO.code && accountDTO.code !== oldAccount.code) {
|
|
||||||
await this.validator.isAccountCodeUniqueOrThrowError(
|
|
||||||
accountDTO.code,
|
|
||||||
oldAccount.id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Retrieve the parent account of throw not found service error.
|
// Retrieve the parent account of throw not found service error.
|
||||||
if (accountDTO.parentAccountId) {
|
if (accountDTO.parentAccountId) {
|
||||||
const parentAccount = await this.validator.getParentAccountOrThrowError(
|
const parentAccount = await this.validator.getParentAccountOrThrowError(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export const ERRORS = {
|
|||||||
ACCOUNT_TYPE_NOT_FOUND: 'account_type_not_found',
|
ACCOUNT_TYPE_NOT_FOUND: 'account_type_not_found',
|
||||||
PARENT_ACCOUNT_NOT_FOUND: 'parent_account_not_found',
|
PARENT_ACCOUNT_NOT_FOUND: 'parent_account_not_found',
|
||||||
ACCOUNT_CODE_NOT_UNIQUE: 'account_code_not_unique',
|
ACCOUNT_CODE_NOT_UNIQUE: 'account_code_not_unique',
|
||||||
|
ACCOUNT_CODE_REQUIRED: 'account_code_required',
|
||||||
ACCOUNT_NAME_NOT_UNIQUE: 'account_name_not_unqiue',
|
ACCOUNT_NAME_NOT_UNIQUE: 'account_name_not_unqiue',
|
||||||
PARENT_ACCOUNT_HAS_DIFFERENT_TYPE: 'parent_has_different_type',
|
PARENT_ACCOUNT_HAS_DIFFERENT_TYPE: 'parent_has_different_type',
|
||||||
ACCOUNT_TYPE_NOT_ALLOWED_TO_CHANGE: 'account_type_not_allowed_to_changed',
|
ACCOUNT_TYPE_NOT_ALLOWED_TO_CHANGE: 'account_type_not_allowed_to_changed',
|
||||||
|
|||||||
@@ -15,10 +15,16 @@ export const AccountDialogAction = {
|
|||||||
*/
|
*/
|
||||||
export const transformApiErrors = (errors) => {
|
export const transformApiErrors = (errors) => {
|
||||||
const fields = {};
|
const fields = {};
|
||||||
|
if (errors.find((e) => e.type === 'account_code_required')) {
|
||||||
|
fields.code = intl.get('account_code_is_required');
|
||||||
|
}
|
||||||
if (errors.find((e) => e.type === 'NOT_UNIQUE_CODE')) {
|
if (errors.find((e) => e.type === 'NOT_UNIQUE_CODE')) {
|
||||||
fields.code = intl.get('account_code_is_not_unique');
|
fields.code = intl.get('account_code_is_not_unique');
|
||||||
}
|
}
|
||||||
if (errors.find((e) => e.type === 'ACCOUNT.NAME.NOT.UNIQUE')) {
|
if (errors.find((e) => e.type === 'account_code_not_unique')) {
|
||||||
|
fields.code = intl.get('account_code_is_not_unique');
|
||||||
|
}
|
||||||
|
if (errors.find((e) => e.type === 'account_name_not_unqiue')) {
|
||||||
fields.name = intl.get('account_name_is_already_used');
|
fields.name = intl.get('account_name_is_already_used');
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -462,6 +462,7 @@
|
|||||||
"should_total_of_credit_and_debit_be_equal": "Should total of credit and debit be equal.",
|
"should_total_of_credit_and_debit_be_equal": "Should total of credit and debit be equal.",
|
||||||
"no_accounts": "No Accounts",
|
"no_accounts": "No Accounts",
|
||||||
"the_accounts_have_been_successfully_inactivated": "The accounts have been successfully inactivated.",
|
"the_accounts_have_been_successfully_inactivated": "The accounts have been successfully inactivated.",
|
||||||
|
"account_code_is_required": "Account code is required.",
|
||||||
"account_code_is_not_unique": "Account code is not unique.",
|
"account_code_is_not_unique": "Account code is not unique.",
|
||||||
"are_sure_to_publish_this_expense": "Are you sure you want to publish this expense?",
|
"are_sure_to_publish_this_expense": "Are you sure you want to publish this expense?",
|
||||||
"once_delete_these_journals_you_will_not_able_restore_them": "Once you delete these journals, you won't be able to retrieve them later. Are you sure you want to delete them?",
|
"once_delete_these_journals_you_will_not_able_restore_them": "Once you delete these journals, you won't be able to retrieve them later. Are you sure you want to delete them?",
|
||||||
|
|||||||
Reference in New Issue
Block a user