Compare commits

..

1 Commits

Author SHA1 Message Date
a.bouhuolia
a854b42ce5 asdfasdF 2023-02-13 21:29:07 +02:00
15 changed files with 72 additions and 5873 deletions

5697
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,6 @@ import DynamicListingService from '@/services/DynamicListing/DynamicListService'
import { DATATYPES_LENGTH } from '@/data/DataTypes'; import { DATATYPES_LENGTH } from '@/data/DataTypes';
import CheckPolicies from '@/api/middleware/CheckPolicies'; import CheckPolicies from '@/api/middleware/CheckPolicies';
import { AccountsApplication } from '@/services/Accounts/AccountsApplication'; import { AccountsApplication } from '@/services/Accounts/AccountsApplication';
import { MAX_ACCOUNTS_CHART_DEPTH } from 'services/Accounts/constants';
@Service() @Service()
export default class AccountsController extends BaseController { export default class AccountsController extends BaseController {
@@ -495,22 +494,6 @@ 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); next(error);
} }

View File

@@ -5,6 +5,9 @@ import './before';
import express from 'express'; import express from 'express';
import loadersFactory from 'loaders'; import loadersFactory from 'loaders';
console.log("asdfasf");
async function startServer() { async function startServer() {
const app = express(); const app = express();

View File

@@ -3,7 +3,7 @@ import TenancyService from '@/services/Tenancy/TenancyService';
import { ServiceError } from '@/exceptions'; import { ServiceError } from '@/exceptions';
import { IAccountDTO, IAccount, IAccountCreateDTO } from '@/interfaces'; import { IAccountDTO, IAccount, IAccountCreateDTO } from '@/interfaces';
import AccountTypesUtils from '@/lib/AccountTypes'; import AccountTypesUtils from '@/lib/AccountTypes';
import { ERRORS, MAX_ACCOUNTS_CHART_DEPTH } from './constants'; import { ERRORS } from './constants';
@Service() @Service()
export class CommandAccountValidators { export class CommandAccountValidators {
@@ -154,13 +154,13 @@ export class CommandAccountValidators {
* parent account. * parent account.
* @param {IAccountCreateDTO} accountDTO * @param {IAccountCreateDTO} accountDTO
* @param {IAccount} parentAccount * @param {IAccount} parentAccount
* @param {string} baseCurrency - * @param {string} baseCurrency -
* @throws {ServiceError(ERRORS.ACCOUNT_CURRENCY_NOT_SAME_PARENT_ACCOUNT)} * @throws {ServiceError(ERRORS.ACCOUNT_CURRENCY_NOT_SAME_PARENT_ACCOUNT)}
*/ */
public validateCurrentSameParentAccount = ( public validateCurrentSameParentAccount = (
accountDTO: IAccountCreateDTO, accountDTO: IAccountCreateDTO,
parentAccount: IAccount, parentAccount: IAccount,
baseCurrency: string baseCurrency: string,
) => { ) => {
// If the account DTO currency not assigned and the parent account has no base currency. // If the account DTO currency not assigned and the parent account has no base currency.
if ( if (
@@ -208,24 +208,4 @@ export class CommandAccountValidators {
} }
return account; 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);
}
}
} }

View File

@@ -70,11 +70,6 @@ export class CreateAccount {
parentAccount, parentAccount,
baseCurrency 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. // Validates the given account type supports the multi-currency.
this.validator.validateAccountTypeSupportCurrency(accountDTO, baseCurrency); this.validator.validateAccountTypeSupportCurrency(accountDTO, baseCurrency);

View File

@@ -5,7 +5,6 @@ import TenancyService from '@/services/Tenancy/TenancyService';
import DynamicListingService from '@/services/DynamicListing/DynamicListService'; import DynamicListingService from '@/services/DynamicListing/DynamicListService';
import { AccountTransformer } from './AccountTransform'; import { AccountTransformer } from './AccountTransform';
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
import { flatToNestedArray } from '@/utils';
@Service() @Service()
export class GetAccounts { export class GetAccounts {
@@ -54,17 +53,11 @@ export class GetAccounts {
builder.modify('inactiveMode', filter.inactiveMode); builder.modify('inactiveMode', filter.inactiveMode);
}); });
// Retrievs the formatted accounts collection. // Retrievs the formatted accounts collection.
const preTransformedAccounts = await this.transformer.transform( const transformedAccounts = await this.transformer.transform(
tenantId, tenantId,
accounts, accounts,
new AccountTransformer() new AccountTransformer()
); );
// Transform accounts to nested array.
const transformedAccounts = flatToNestedArray(preTransformedAccounts, {
id: 'id',
parentId: 'parentAccountId',
});
return { return {
accounts: transformedAccounts, accounts: transformedAccounts,
filterMeta: dynamicList.getResponseMeta(), filterMeta: dynamicList.getResponseMeta(),

View File

@@ -13,12 +13,8 @@ export const ERRORS = {
CLOSE_ACCOUNT_AND_TO_ACCOUNT_NOT_SAME_TYPE: CLOSE_ACCOUNT_AND_TO_ACCOUNT_NOT_SAME_TYPE:
'close_account_and_to_account_not_same_type', 'close_account_and_to_account_not_same_type',
ACCOUNTS_NOT_FOUND: 'accounts_not_found', ACCOUNTS_NOT_FOUND: 'accounts_not_found',
ACCOUNT_TYPE_NOT_SUPPORTS_MULTI_CURRENCY: ACCOUNT_TYPE_NOT_SUPPORTS_MULTI_CURRENCY: '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_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. // Default views columns.
@@ -31,8 +27,6 @@ export const DEFAULT_VIEW_COLUMNS = [
{ key: 'currencyCode', label: 'Currency' }, { key: 'currencyCode', label: 'Currency' },
]; ];
export const MAX_ACCOUNTS_CHART_DEPTH = 5;
// Accounts default views. // Accounts default views.
export const DEFAULT_VIEWS = [ export const DEFAULT_VIEWS = [
{ {
@@ -49,12 +43,7 @@ export const DEFAULT_VIEWS = [
slug: 'liabilities', slug: 'liabilities',
rolesLogicExpression: '1', rolesLogicExpression: '1',
roles: [ roles: [
{ { fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'liability' },
fieldKey: 'root_type',
index: 1,
comparator: 'equals',
value: 'liability',
},
], ],
columns: DEFAULT_VIEW_COLUMNS, columns: DEFAULT_VIEW_COLUMNS,
}, },
@@ -63,12 +52,7 @@ export const DEFAULT_VIEWS = [
slug: 'equity', slug: 'equity',
rolesLogicExpression: '1', rolesLogicExpression: '1',
roles: [ roles: [
{ { fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'equity' },
fieldKey: 'root_type',
index: 1,
comparator: 'equals',
value: 'equity',
},
], ],
columns: DEFAULT_VIEW_COLUMNS, columns: DEFAULT_VIEW_COLUMNS,
}, },
@@ -77,12 +61,7 @@ export const DEFAULT_VIEWS = [
slug: 'income', slug: 'income',
rolesLogicExpression: '1', rolesLogicExpression: '1',
roles: [ roles: [
{ { fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'income' },
fieldKey: 'root_type',
index: 1,
comparator: 'equals',
value: 'income',
},
], ],
columns: DEFAULT_VIEW_COLUMNS, columns: DEFAULT_VIEW_COLUMNS,
}, },
@@ -91,12 +70,7 @@ export const DEFAULT_VIEWS = [
slug: 'expenses', slug: 'expenses',
rolesLogicExpression: '1', rolesLogicExpression: '1',
roles: [ roles: [
{ { fieldKey: 'root_type', index: 1, comparator: 'equals', value: 'expense' },
fieldKey: 'root_type',
index: 1,
comparator: 'equals',
value: 'expense',
},
], ],
columns: DEFAULT_VIEW_COLUMNS, columns: DEFAULT_VIEW_COLUMNS,
}, },

View File

@@ -99,7 +99,7 @@
"yup": "^0.28.1" "yup": "^0.28.1"
}, },
"scripts": { "scripts": {
"dev": "PORT=4000 craco start", "dev": "craco start",
"build": "craco build", "build": "craco build",
"test": "node scripts/test.js", "test": "node scripts/test.js",
"storybook": "start-storybook -p 6006" "storybook": "start-storybook -p 6006"

View File

@@ -72,7 +72,6 @@ export default function TableCell({ cell, row, index }) {
[`td-${cell.column.id}`]: cell.column.id, [`td-${cell.column.id}`]: cell.column.id,
[`td-${cellType}-type`]: !!cellType, [`td-${cellType}-type`]: !!cellType,
}), }),
tabindex: 0,
onClick: handleCellClick, onClick: handleCellClick,
})} })}
> >

View File

@@ -3,6 +3,7 @@ import React, { useCallback } from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { Formik } from 'formik'; import { Formik } from 'formik';
import { omit } from 'lodash';
import { AppToaster } from '@/components'; import { AppToaster } from '@/components';
import AccountDialogFormContent from './AccountDialogFormContent'; import AccountDialogFormContent from './AccountDialogFormContent';
@@ -13,11 +14,7 @@ import {
CreateAccountFormSchema, CreateAccountFormSchema,
} from './AccountForm.schema'; } from './AccountForm.schema';
import { compose, transformToForm } from '@/utils'; import { compose, transformToForm } from '@/utils';
import { import { transformApiErrors, transformAccountToForm } from './utils';
transformApiErrors,
transformAccountToForm,
transformFormToReq,
} from './utils';
import '@/style/pages/Accounts/AccountFormDialog.scss'; import '@/style/pages/Accounts/AccountFormDialog.scss';
import { useAccountDialogContext } from './AccountDialogProvider'; import { useAccountDialogContext } from './AccountDialogProvider';
@@ -29,7 +26,7 @@ const defaultInitialValues = {
name: '', name: '',
code: '', code: '',
description: '', description: '',
currency_code: '', currency_code:'',
subaccount: false, subaccount: false,
}; };
@@ -46,6 +43,7 @@ function AccountFormDialogContent({
createAccountMutate, createAccountMutate,
account, account,
accountId,
payload, payload,
isNewMode, isNewMode,
dialogName, dialogName,
@@ -58,7 +56,7 @@ function AccountFormDialogContent({
// Callbacks handles form submit. // Callbacks handles form submit.
const handleFormSubmit = (values, { setSubmitting, setErrors }) => { const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
const form = transformFormToReq(values); const form = omit(values, ['subaccount']);
const toastAccountName = values.code const toastAccountName = values.code
? `${values.code} - ${values.name}` ? `${values.code} - ${values.name}`
: values.name; : values.name;
@@ -92,8 +90,8 @@ function AccountFormDialogContent({
setErrors({ ...errorsTransformed }); setErrors({ ...errorsTransformed });
setSubmitting(false); setSubmitting(false);
}; };
if (payload.accountId) { if (accountId) {
editAccountMutate([payload.accountId, form]) editAccountMutate([accountId, form])
.then(handleSuccess) .then(handleSuccess)
.catch(handleError); .catch(handleError);
} else { } else {
@@ -115,6 +113,7 @@ function AccountFormDialogContent({
defaultInitialValues, defaultInitialValues,
), ),
}; };
// Handles dialog close. // Handles dialog close.
const handleClose = useCallback(() => { const handleClose = useCallback(() => {
closeDialog(dialogName); closeDialog(dialogName);

View File

@@ -26,7 +26,6 @@ import { inputIntent, compose } from '@/utils';
import { useAutofocus } from '@/hooks'; import { useAutofocus } from '@/hooks';
import { FOREIGN_CURRENCY_ACCOUNTS } from '@/constants/accountTypes'; import { FOREIGN_CURRENCY_ACCOUNTS } from '@/constants/accountTypes';
import { useAccountDialogContext } from './AccountDialogProvider'; import { useAccountDialogContext } from './AccountDialogProvider';
import { parentAccountShouldUpdate } from './utils';
/** /**
* Account form dialogs fields. * Account form dialogs fields.
@@ -116,7 +115,12 @@ function AccountFormDialogFields({
> >
<Checkbox <Checkbox
inline={true} inline={true}
label={<T id={'sub_account'} />} label={
<>
<T id={'sub_account'} />
<Hint />
</>
}
name={'subaccount'} name={'subaccount'}
{...field} {...field}
/> />
@@ -124,36 +128,37 @@ function AccountFormDialogFields({
)} )}
</Field> </Field>
<FastField <If condition={values.subaccount}>
name={'parent_account_id'} <FastField name={'parent_account_id'}>
shouldUpdate={parentAccountShouldUpdate} {({
> form: { values, setFieldValue },
{({ field: { value },
form: { values, setFieldValue }, meta: { error, touched },
field: { value }, }) => (
meta: { error, touched }, <FormGroup
}) => ( label={<T id={'parent_account'} />}
<FormGroup className={classNames(
label={<T id={'parent_account'} />} 'form-group--parent-account',
className={classNames('form-group--parent-account', Classes.FILL)} Classes.FILL,
inline={true} )}
intent={inputIntent({ error, touched })} inline={true}
helperText={<ErrorMessage name="parent_account_id" />} intent={inputIntent({ error, touched })}
> helperText={<ErrorMessage name="parent_account_id" />}
<AccountsSelectList >
accounts={accounts} <AccountsSelectList
onAccountSelected={(account) => { accounts={accounts}
setFieldValue('parent_account_id', account.id); onAccountSelected={(account) => {
}} setFieldValue('parent_account_id', account.id);
defaultSelectText={<T id={'select_parent_account'} />} }}
selectedAccountId={value} defaultSelectText={<T id={'select_parent_account'} />}
popoverFill={true} selectedAccountId={value}
filterByTypes={values.account_type} popoverFill={true}
disabled={!values.subaccount} filterByTypes={values.account_type}
/> />
</FormGroup> </FormGroup>
)} )}
</FastField> </FastField>
</If>
<If condition={FOREIGN_CURRENCY_ACCOUNTS.includes(values.account_type)}> <If condition={FOREIGN_CURRENCY_ACCOUNTS.includes(values.account_type)}>
{/*------------ Currency -----------*/} {/*------------ Currency -----------*/}

View File

@@ -2,7 +2,6 @@
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import * as R from 'ramda'; import * as R from 'ramda';
import { isUndefined } from 'lodash'; import { isUndefined } from 'lodash';
import { defaultFastFieldShouldUpdate } from '@/utils';
export const AccountDialogAction = { export const AccountDialogAction = {
Edit: 'edit', Edit: 'edit',
@@ -34,7 +33,7 @@ export const transformApiErrors = (errors) => {
/** /**
* Payload transformer in account edit mode. * Payload transformer in account edit mode.
*/ */
function tranformNewChildAccountPayload(account, payload) { function tranformNewChildAccountPayload(payload) {
return { return {
parent_account_id: payload.parentAccountId || '', parent_account_id: payload.parentAccountId || '',
account_type: payload.accountType || '', account_type: payload.accountType || '',
@@ -45,7 +44,7 @@ function tranformNewChildAccountPayload(account, payload) {
/** /**
* Payload transformer in new account with defined type. * Payload transformer in new account with defined type.
*/ */
function transformNewDefinedTypePayload(account, payload) { function transformNewDefinedTypePayload(payload) {
return { return {
account_type: payload.accountType || '', account_type: payload.accountType || '',
}; };
@@ -64,9 +63,7 @@ const mergeWithAccount = R.curry((transformed, account) => {
/** /**
* Default account payload transformer. * Default account payload transformer.
*/ */
const defaultPayloadTransform = (account, payload) => ({ const defaultPayloadTransform = () => ({});
subaccount: !!account.parent_account_id,
});
/** /**
* Defined payload transformers. * Defined payload transformers.
@@ -92,7 +89,7 @@ export const transformAccountToForm = (account, payload) => {
return [ return [
condition[0] === payload.action ? R.T : R.F, condition[0] === payload.action ? R.T : R.F,
mergeWithAccount(transformer(account, payload)), mergeWithAccount(transformer(payload)),
]; ];
}); });
return R.cond(results)(account); return R.cond(results)(account);
@@ -109,29 +106,3 @@ export const getDisabledFormFields = (account, payload) => {
payload.action === AccountDialogAction.NewDefinedType, payload.action === AccountDialogAction.NewDefinedType,
}; };
}; };
/**
* Detarmines whether should update the parent account field.
* @param newProps
* @param oldProps
* @returns {boolean}
*/
export const parentAccountShouldUpdate = (newProps, oldProps) => {
return (
newProps.formik.values.subaccount !== oldProps.formik.values.subaccount ||
defaultFastFieldShouldUpdate(newProps, oldProps)
);
};
/**
* Transformes the form values to the request.
*/
export const transformFormToReq = (form) => {
return R.compose(
R.omit(['subaccount']),
R.when(
R.propSatisfies(R.equals(R.__, false), 'subaccount'),
R.assoc(['parent_account_id'], ''),
),
)(form);
};

View File

@@ -105,7 +105,7 @@ export function ItemsActionMenuList({
</Can> </Can>
<Can I={ItemAction.Create} a={AbilitySubject.Item}> <Can I={ItemAction.Create} a={AbilitySubject.Item}>
<MenuItem <MenuItem
icon={<Icon icon="content-copy" iconSize={16} />} icon={<Icon icon="duplicate-16" />}
text={intl.get('duplicate')} text={intl.get('duplicate')}
onClick={safeCallback(onDuplicate, original)} onClick={safeCallback(onDuplicate, original)}
/> />

View File

@@ -559,10 +559,4 @@ export default {
], ],
viewBox: '0 0 24 24', viewBox: '0 0 24 24',
}, },
'content-copy': {
path: [
'M15 0H5c-.55 0-1 .45-1 1v2h2V2h8v7h-1v2h2c.55 0 1-.45 1-1V1c0-.55-.45-1-1-1zm-4 4H1c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h10c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1zm-1 10H2V6h8v8z'
],
viewBox: '0 0 16 16'
}
}; };

View File

@@ -10,7 +10,7 @@
display: block; display: block;
.thead .thead-inner, .thead .thead-inner,
.tbody .tbody-inner { .tbody .tbody-inner{
min-width: fit-content; min-width: fit-content;
} }
@@ -25,7 +25,7 @@
font-weight: 400; font-weight: 400;
border-bottom: 1px solid #d2dde2; border-bottom: 1px solid #d2dde2;
> div { >div {
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
@@ -208,10 +208,6 @@
width: 100%; width: 100%;
} }
} }
&:focus {
outline: 1px solid rgba(0, 82, 204, 0.7);
outline-offset: -1px;
}
} }
.tr:hover .td { .tr:hover .td {
@@ -361,9 +357,13 @@
position: sticky; position: sticky;
} }
[data-sticky-last-left-td] {} [data-sticky-last-left-td] {
[data-sticky-first-right-td] {} }
[data-sticky-first-right-td] {
}
} }
&.has-virtualized-rows { &.has-virtualized-rows {