diff --git a/client/src/containers/Dialogs/CurrencyFormDialog/CurrencyFormFields.js b/client/src/containers/Dialogs/CurrencyFormDialog/CurrencyFormFields.js
index c24588e34..beb88ef24 100644
--- a/client/src/containers/Dialogs/CurrencyFormDialog/CurrencyFormFields.js
+++ b/client/src/containers/Dialogs/CurrencyFormDialog/CurrencyFormFields.js
@@ -2,7 +2,9 @@ import React from 'react';
import { Classes, FormGroup, InputGroup } from '@blueprintjs/core';
import { FastField } from 'formik';
import { FormattedMessage as T } from 'react-intl';
+import classNames from 'classnames';
+import { CLASSES } from 'common/classes';
import { useCurrencyFormContext } from './CurrencyFormProvider';
import { ErrorMessage, FieldRequiredHint, ListSelect } from 'components';
@@ -25,7 +27,7 @@ export default function CurrencyFormFields() {
field: { value },
meta: { error, touched },
}) => (
-
+
)}
diff --git a/client/src/containers/Dialogs/CurrencyFormDialog/index.js b/client/src/containers/Dialogs/CurrencyFormDialog/index.js
index 5125602b9..6a5fe2b8b 100644
--- a/client/src/containers/Dialogs/CurrencyFormDialog/index.js
+++ b/client/src/containers/Dialogs/CurrencyFormDialog/index.js
@@ -30,7 +30,7 @@ function CurrencyFormDialog({
isOpen={isOpen}
autoFocus={true}
canEscapeKeyClose={true}
- style={{ width: '450px' }}
+ style={{ width: '400px' }}
>
{
const options = transformToOptions(values);
+
setSubmitting(true);
const onSuccess = () => {
AppToaster.show({
@@ -68,21 +68,12 @@ function AccountantFormPage({
};
return (
-
+
);
}
diff --git a/client/src/containers/Preferences/Accountant/AccountantFormProvider.js b/client/src/containers/Preferences/Accountant/AccountantFormProvider.js
index a104ed451..9e2305120 100644
--- a/client/src/containers/Preferences/Accountant/AccountantFormProvider.js
+++ b/client/src/containers/Preferences/Accountant/AccountantFormProvider.js
@@ -1,6 +1,8 @@
import React from 'react';
-import { LoadingIndicator } from 'components';
+import classNames from 'classnames';
+import { CLASSES } from 'common/classes';
import { useAccounts, useSaveSettings, useSettings } from 'hooks/query';
+import PreferencesPageLoader from '../PreferencesPageLoader';
const AccountantFormContext = React.createContext();
@@ -24,10 +26,23 @@ function AccountantFormProvider({ ...props }) {
saveSettingMutate,
};
+ const isLoading = isSettingsLoading || isAccountsLoading;
+
return (
-
-
-
+
+
+ {isLoading ? (
+
+ ) : (
+
+ )}
+
+
);
}
diff --git a/client/src/containers/Preferences/General/GeneralForm.js b/client/src/containers/Preferences/General/GeneralForm.js
index 44f6f13c8..856a9a8e5 100644
--- a/client/src/containers/Preferences/General/GeneralForm.js
+++ b/client/src/containers/Preferences/General/GeneralForm.js
@@ -36,220 +36,224 @@ export default function PreferencesGeneralForm({}) {
return (
- )}
-
+
+ {({ field, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ inline={true}
+ intent={inputIntent({ error, touched })}
+ className={'form-group--org-name'}
+ helperText={'Shown on sales forms and purchase orders.'}
+ >
+
+
+ )}
+
-
- {({ form, field: { value }, meta: { error, touched } }) => (
- }
- labelInfo={}
- inline={true}
- intent={inputIntent({ error, touched })}
- className={classNames('form-group--select-list', CLASSES.FILL)}
- helperText={'For reporting, you can specify any month as the start of your financial year (also called your financial reporting year or accounting year).'}
- >
- {
- form.setFieldValue('financial_date_start', formattedDate);
- })}
- popoverProps={{ position: Position.BOTTOM, minimal: true }}
- />
-
- )}
-
-
-
- {({ field, meta: { error, touched } }) => (
- }
- inline={true}
- intent={inputIntent({ error, touched })}
- helperText={}
- className={'form-group--org-industry'}
- >
-
-
- )}
-
-
-
- {({ form, field: { value }, meta: { error, touched } }) => (
- }
- className={classNames(
- 'form-group--business-location',
- CLASSES.FILL,
- )}
- inline={true}
- helperText={}
- intent={inputIntent({ error, touched })}
- >
- {
- form.setFieldValue('location', value);
- }}
- selectedItem={value}
- selectedItemProp={'value'}
- defaultText={}
- textProp={'name'}
- popoverProps={{ minimal: true }}
- />
-
- )}
-
-
-
- {({ form, field: { value }, meta: { error, touched } }) => (
- }
- labelInfo={}
- className={classNames('form-group--base-currency', CLASSES.FILL)}
- inline={true}
- intent={inputIntent({ error, touched })}
- helperText={"You can't change the base currency as there are transactions recorded in your organization."}
- >
- {
- form.setFieldValue('base_currency', currency.code);
- }}
- selectedItem={value}
- selectedItemProp={'code'}
- defaultText={}
- textProp={'name'}
- labelProp={'code'}
- popoverProps={{ minimal: true }}
- />
-
- )}
-
-
-
- {({ form, field: { value }, meta: { error, touched } }) => (
- }
- labelInfo={}
- className={classNames('form-group--fiscal-year', CLASSES.FILL)}
- inline={true}
- helperText={}
- intent={inputIntent({ error, touched })}
- >
-
- form.setFieldValue('fiscal_year', value)
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ inline={true}
+ intent={inputIntent({ error, touched })}
+ className={classNames('form-group--select-list', CLASSES.FILL)}
+ helperText={
+ 'For reporting, you can specify any month as the start of your financial year (also called your financial reporting year or accounting year).'
}
- selectedItem={value}
- selectedItemProp={'value'}
- defaultText={}
- textProp={'name'}
- popoverProps={{ minimal: true }}
- />
-
- )}
-
+ >
+ {
+ form.setFieldValue('financial_date_start', formattedDate);
+ })}
+ popoverProps={{ position: Position.BOTTOM, minimal: true }}
+ />
+
+ )}
+
-
- {({ form, field: { value }, meta: { error, touched } }) => (
- }
- labelInfo={}
- inline={true}
- className={classNames('form-group--language', CLASSES.FILL)}
- intent={inputIntent({ error, touched })}
- helperText={}
- >
- }
- selectedItem={value}
- onItemSelect={(item) =>
- form.setFieldValue('language', item.value)
+
+ {({ field, meta: { error, touched } }) => (
+ }
+ inline={true}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ className={'form-group--org-industry'}
+ >
+
+
+ )}
+
+
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ className={classNames(
+ 'form-group--business-location',
+ CLASSES.FILL,
+ )}
+ inline={true}
+ helperText={}
+ intent={inputIntent({ error, touched })}
+ >
+ {
+ form.setFieldValue('location', value);
+ }}
+ selectedItem={value}
+ selectedItemProp={'value'}
+ defaultText={}
+ textProp={'name'}
+ popoverProps={{ minimal: true }}
+ />
+
+ )}
+
+
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ className={classNames('form-group--base-currency', CLASSES.FILL)}
+ inline={true}
+ intent={inputIntent({ error, touched })}
+ helperText={
+ "You can't change the base currency as there are transactions recorded in your organization."
}
- popoverProps={{ minimal: true }}
- />
-
- )}
-
+ >
+ {
+ form.setFieldValue('base_currency', currency.code);
+ }}
+ selectedItem={value}
+ selectedItemProp={'code'}
+ defaultText={}
+ textProp={'name'}
+ labelProp={'code'}
+ popoverProps={{ minimal: true }}
+ />
+
+ )}
+
-
- {({ form, field: { value }, meta: { error, touched } }) => (
- }
- labelInfo={}
- inline={true}
- className={classNames(
- 'form-group--time-zone',
- CLASSES.FORM_GROUP_LIST_SELECT,
- CLASSES.FILL,
- )}
- intent={inputIntent({ error, touched })}
- helperText={}
- >
- {
- form.setFieldValue('time_zone', timezone);
- }}
- valueDisplayFormat="composite"
- placeholder={}
- />
-
- )}
-
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ className={classNames('form-group--fiscal-year', CLASSES.FILL)}
+ inline={true}
+ helperText={}
+ intent={inputIntent({ error, touched })}
+ >
+
+ form.setFieldValue('fiscal_year', value)
+ }
+ selectedItem={value}
+ selectedItemProp={'value'}
+ defaultText={}
+ textProp={'name'}
+ popoverProps={{ minimal: true }}
+ />
+
+ )}
+
-
- {({ form, field: { value }, meta: { error, touched } }) => (
- }
- labelInfo={}
- inline={true}
- className={classNames('form-group--date-format', CLASSES.FILL)}
- intent={inputIntent({ error, touched })}
- helperText={}
- >
- {
- form.setFieldValue('date_format', dateFormat.value);
- }}
- selectedItem={value}
- selectedItemProp={'value'}
- defaultText={}
- textProp={'name'}
- labelProp={'label'}
- popoverProps={{ minimal: true }}
- />
-
- )}
-
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ inline={true}
+ className={classNames('form-group--language', CLASSES.FILL)}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ >
+ }
+ selectedItem={value}
+ onItemSelect={(item) =>
+ form.setFieldValue('language', item.value)
+ }
+ popoverProps={{ minimal: true }}
+ />
+
+ )}
+
-
-
-
-
-
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ inline={true}
+ className={classNames(
+ 'form-group--time-zone',
+ CLASSES.FORM_GROUP_LIST_SELECT,
+ CLASSES.FILL,
+ )}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ >
+ {
+ form.setFieldValue('time_zone', timezone);
+ }}
+ valueDisplayFormat="composite"
+ placeholder={}
+ />
+
+ )}
+
+
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ inline={true}
+ className={classNames('form-group--date-format', CLASSES.FILL)}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ >
+ {
+ form.setFieldValue('date_format', dateFormat.value);
+ }}
+ selectedItem={value}
+ selectedItemProp={'value'}
+ defaultText={}
+ textProp={'name'}
+ labelProp={'label'}
+ popoverProps={{ minimal: true }}
+ />
+
+ )}
+
+
+
+
+
+
+
);
}
diff --git a/client/src/containers/Preferences/General/GeneralFormPage.js b/client/src/containers/Preferences/General/GeneralFormPage.js
index a003ac049..72cd456bb 100644
--- a/client/src/containers/Preferences/General/GeneralFormPage.js
+++ b/client/src/containers/Preferences/General/GeneralFormPage.js
@@ -2,9 +2,7 @@ import React, { useEffect } from 'react';
import { Formik } from 'formik';
import { mapKeys, snakeCase } from 'lodash';
import { Intent } from '@blueprintjs/core';
-import classNames from 'classnames';
import { useIntl } from 'react-intl';
-import { CLASSES } from 'common/classes';
import { AppToaster } from 'components';
import GeneralForm from './GeneralForm';
@@ -46,6 +44,7 @@ function GeneralFormPage({
const options = optionsMapToArray(values).map((option) => {
return { key: option.key, ...option, group: 'organization' };
});
+ // Handle request success.
const onSuccess = (response) => {
AppToaster.show({
message: 'The general preferences has been saved.',
@@ -54,6 +53,7 @@ function GeneralFormPage({
setSubmitting(false);
resetForm();
};
+ // Handle request error.
const onError = (errors) => {
setSubmitting(false);
};
@@ -61,21 +61,12 @@ function GeneralFormPage({
};
return (
-
+
);
}
diff --git a/client/src/containers/Preferences/General/GeneralFormProvider.js b/client/src/containers/Preferences/General/GeneralFormProvider.js
index 03b01c69d..a6c114f2f 100644
--- a/client/src/containers/Preferences/General/GeneralFormProvider.js
+++ b/client/src/containers/Preferences/General/GeneralFormProvider.js
@@ -1,6 +1,8 @@
import React, { createContext } from 'react';
-import { LoadingIndicator } from 'components';
+import classNames from 'classnames';
+import { CLASSES } from 'common/classes';
import { useSaveSettings, useSettings } from 'hooks/query';
+import PreferencesPageLoader from '../PreferencesPageLoader';
const GeneralFormContext = createContext();
@@ -8,8 +10,8 @@ const GeneralFormContext = createContext();
* General form provider.
*/
function GeneralFormProvider({ ...props }) {
- //Fetches Organization Settings.
- const { isFetching: isSettingsLoading } = useSettings();
+ // Fetches Organization Settings.
+ const { isLoading: isSettingsLoading } = useSettings();
// Save Organization Settings.
const { mutateAsync: saveSettingMutate } = useSaveSettings();
@@ -20,10 +22,23 @@ function GeneralFormProvider({ ...props }) {
saveSettingMutate,
};
+ const loading = isSettingsLoading;
+
return (
-
-
-
+
+
+ {loading ? (
+
+ ) : (
+
+ )}
+
+
);
}
diff --git a/client/src/containers/Preferences/Item/Item.schema.js b/client/src/containers/Preferences/Item/Item.schema.js
index ccb5698f2..5a032f4d1 100644
--- a/client/src/containers/Preferences/Item/Item.schema.js
+++ b/client/src/containers/Preferences/Item/Item.schema.js
@@ -1,9 +1,9 @@
import * as Yup from 'yup';
const Schema = Yup.object().shape({
- sell_account: Yup.number().nullable().required(),
- cost_account: Yup.number().nullable().required(),
- inventory_account: Yup.number().nullable().required(),
+ sell_account: Yup.number().nullable(),
+ cost_account: Yup.number().nullable(),
+ inventory_account: Yup.number().nullable(),
});
export const ItemPreferencesSchema = Schema;
diff --git a/client/src/containers/Preferences/Item/ItemFormPage.js b/client/src/containers/Preferences/Item/ItemFormPage.js
index e9bbdcfc2..512d5d732 100644
--- a/client/src/containers/Preferences/Item/ItemFormPage.js
+++ b/client/src/containers/Preferences/Item/ItemFormPage.js
@@ -1,8 +1,6 @@
import React, { useEffect } from 'react';
import { Formik } from 'formik';
import { Intent } from '@blueprintjs/core';
-import classNames from 'classnames';
-import { CLASSES } from 'common/classes';
import { AppToaster } from 'components';
import { useIntl } from 'react-intl';
import { ItemPreferencesSchema } from './Item.schema';
@@ -19,24 +17,28 @@ import 'style/pages/Preferences/Accounting.scss';
function ItemFormPage({
// #withSettings
itemsSettings,
- //# withDashboardActions
+
+ // #withDashboardActions
changePreferencesPageTitle,
}) {
const { formatMessage } = useIntl();
const { saveSettingMutate } = useItemFormContext();
const initialValues = {
+ sell_account: '',
+ cost_account: '',
+ inventory_account: '',
...transformGeneralSettings(itemsSettings),
};
useEffect(() => {
changePreferencesPageTitle(formatMessage({ id: 'items' }));
- }, [changePreferencesPageTitle]);
+ }, [formatMessage, changePreferencesPageTitle]);
+ // Handle form submit.
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
- const options = optionsMapToArray(values).map((option) => {
- return { key: option.key, ...option, group: 'items' };
- });
+ const options = optionsMapToArray(values)
+ .map((option) => ({ ...option, group: 'items' }));
const onSuccess = () => {
AppToaster.show({
@@ -55,21 +57,12 @@ function ItemFormPage({
};
return (
-
+
);
}
diff --git a/client/src/containers/Preferences/Item/ItemFormProvider.js b/client/src/containers/Preferences/Item/ItemFormProvider.js
index a70f27c1a..81b6fa2ce 100644
--- a/client/src/containers/Preferences/Item/ItemFormProvider.js
+++ b/client/src/containers/Preferences/Item/ItemFormProvider.js
@@ -1,7 +1,9 @@
import React, { useContext, createContext } from 'react';
-import { LoadingIndicator } from 'components';
+import classNames from 'classnames';
+import { CLASSES } from 'common/classes';
import { useAccounts, useSaveSettings } from 'hooks/query';
+import PreferencesPageLoader from '../PreferencesPageLoader';
const ItemFormContext = createContext();
@@ -22,10 +24,23 @@ function ItemFormProvider({ ...props }) {
saveSettingMutate,
};
+ const isLoading = isAccountsLoading;
+
return (
-
-
-
+
+
+ {isLoading ? (
+
+ ) : (
+
+ )}
+
+
);
}
diff --git a/client/src/containers/Preferences/PreferencesPageLoader.js b/client/src/containers/Preferences/PreferencesPageLoader.js
new file mode 100644
index 000000000..7694058e2
--- /dev/null
+++ b/client/src/containers/Preferences/PreferencesPageLoader.js
@@ -0,0 +1,23 @@
+import React from 'react';
+import ContentLoader from 'react-content-loader';
+
+export default function PreferencesPageLoader(props) {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/client/src/style/pages/Currency/CurrencyFormDialog.scss b/client/src/style/pages/Currency/CurrencyFormDialog.scss
index 9440a1cdf..ae1ab9d6f 100644
--- a/client/src/style/pages/Currency/CurrencyFormDialog.scss
+++ b/client/src/style/pages/Currency/CurrencyFormDialog.scss
@@ -1,5 +1,7 @@
.dialog--currency-form {
.bp3-dialog-body {
+ margin-bottom: 30px;
+
.bp3-form-group.bp3-inline {
.bp3-label {
min-width: 140px;
diff --git a/server/src/api/controllers/Settings.ts b/server/src/api/controllers/Settings.ts
index 1760eb08f..74a5e31aa 100644
--- a/server/src/api/controllers/Settings.ts
+++ b/server/src/api/controllers/Settings.ts
@@ -41,7 +41,7 @@ export default class SettingsController extends BaseController {
return [
body('options').isArray({ min: 1 }),
body('options.*.key').exists().trim().isLength({ min: 1 }),
- body('options.*.value').exists().trim().isLength({ min: 1 }),
+ body('options.*.value').exists().trim(),
body('options.*.group').exists().trim().isLength({ min: 1 }),
];
}
diff --git a/server/src/database/seeds/core/20200810121809_seed_settings.js b/server/src/database/seeds/core/20200810121809_seed_settings.js
index 62e36cfd7..1eaeec22d 100644
--- a/server/src/database/seeds/core/20200810121809_seed_settings.js
+++ b/server/src/database/seeds/core/20200810121809_seed_settings.js
@@ -5,6 +5,12 @@ exports.up = (knex) => {
const tenancyService = Container.get(TenancyService);
const settings = tenancyService.settings(knex.userParams.tenantId);
+ // Orgnization settings.
+ settings.set({ group: 'organization', key: 'accounting_basis', value: 'accural' });
+
+ // Accounts settings.
+ settings.set({ group: 'accounts', key: 'account_code_unique', value: true });
+
// Manual journals settings.
settings.set({ group: 'manual_journals', key: 'next_number', value: '00001' });
settings.set({ group: 'manual_journals', key: 'auto_increment', value: true });
diff --git a/server/src/services/Currencies/CurrenciesService.ts b/server/src/services/Currencies/CurrenciesService.ts
index adb94cfa5..76f47b218 100644
--- a/server/src/services/Currencies/CurrenciesService.ts
+++ b/server/src/services/Currencies/CurrenciesService.ts
@@ -261,6 +261,7 @@ export default class CurrenciesService implements ICurrenciesService {
await Currency.query().insert({
currency_code: currencyMeta.code,
currency_name: currencyMeta.name,
+ currency_sign: currencyMeta.symbol,
});
}
}