Compare commits

...

4 Commits

Author SHA1 Message Date
a.bouhuolia
05c2232b97 chore(webapp): refactor the setup organization form to use Formik binded component 2023-05-12 12:31:55 +02:00
a.bouhuolia
40bddfdfeb fix(webapp): accrual typo 2023-05-11 21:07:49 +02:00
a.bouhuolia
d6e2f01d70 fix(server): accrual typo 2023-05-11 21:07:01 +02:00
a.bouhuolia
2344d3d34d fix(webapp): general, accoutant and items preferences 2023-05-11 01:47:09 +02:00
27 changed files with 362 additions and 496 deletions

View File

@@ -41,7 +41,7 @@ export default class BalanceSheetStatementController extends BaseFinancialReport
get balanceSheetValidationSchema(): ValidationChain[] { get balanceSheetValidationSchema(): ValidationChain[] {
return [ return [
...this.sheetNumberFormatValidationSchema, ...this.sheetNumberFormatValidationSchema,
query('accounting_method').optional().isIn(['cash', 'accural']), query('accounting_method').optional().isIn(['cash', 'accrual']),
query('from_date').optional(), query('from_date').optional(),
query('to_date').optional(), query('to_date').optional(),

View File

@@ -58,7 +58,7 @@ export default class OrganizationController extends BaseController {
private get organizationValidationSchema(): ValidationChain[] { private get organizationValidationSchema(): ValidationChain[] {
return [ return [
check('name').exists().trim(), check('name').exists().trim(),
check('industry').optional().isString(), check('industry').optional({ nullable: true }).isString().trim().escape(),
check('location').exists().isString().isISO31661Alpha2(), check('location').exists().isString().isISO31661Alpha2(),
check('base_currency').exists().isISO4217(), check('base_currency').exists().isISO4217(),
check('timezone').exists().isIn(moment.tz.names()), check('timezone').exists().isIn(moment.tz.names()),

View File

@@ -8,7 +8,7 @@ export default class SeedSettings extends TenantSeeder {
up() { up() {
const settings = [ const settings = [
// Orgnization settings. // Orgnization settings.
{ group: 'organization', key: 'accounting_basis', value: 'accural' }, { group: 'organization', key: 'accounting_basis', value: 'accrual' },
// Accounts settings. // Accounts settings.
{ group: 'accounts', key: 'account_code_unique', value: true }, { group: 'accounts', key: 'account_code_unique', value: true },

View File

@@ -44,7 +44,7 @@ export interface IBalanceSheetQuery extends IFinancialSheetBranchesQuery {
numberFormat: INumberFormatQuery; numberFormat: INumberFormatQuery;
noneTransactions: boolean; noneTransactions: boolean;
noneZero: boolean; noneZero: boolean;
basis: 'cash' | 'accural'; basis: 'cash' | 'accrual';
accountIds: number[]; accountIds: number[];
percentageOfColumn: boolean; percentageOfColumn: boolean;

View File

@@ -4,7 +4,7 @@ export interface ITrialBalanceSheetQuery {
fromDate: Date | string; fromDate: Date | string;
toDate: Date | string; toDate: Date | string;
numberFormat: INumberFormatQuery; numberFormat: INumberFormatQuery;
basis: 'cash' | 'accural'; basis: 'cash' | 'accrual';
noneZero: boolean; noneZero: boolean;
noneTransactions: boolean; noneTransactions: boolean;
onlyActive: boolean; onlyActive: boolean;

View File

@@ -17,7 +17,7 @@ export const getDefaultPLQuery = (): IProfitLossSheetQuery => ({
formatMoney: 'total', formatMoney: 'total',
precision: 2, precision: 2,
}, },
basis: 'accural', basis: 'accrual',
noneZero: false, noneZero: false,
noneTransactions: false, noneTransactions: false,

View File

@@ -35,7 +35,7 @@ export default class TrialBalanceSheetService extends FinancialSheet {
formatMoney: 'total', formatMoney: 'total',
precision: 2, precision: 2,
}, },
basis: 'accural', basis: 'accrual',
noneZero: false, noneZero: false,
noneTransactions: true, noneTransactions: true,
onlyActive: false, onlyActive: false,

View File

@@ -1205,9 +1205,9 @@
} }
}, },
"@blueprintjs-formik/core": { "@blueprintjs-formik/core": {
"version": "0.2.1", "version": "0.3.3",
"resolved": "https://registry.npmjs.org/@blueprintjs-formik/core/-/core-0.2.1.tgz", "resolved": "https://registry.npmjs.org/@blueprintjs-formik/core/-/core-0.3.3.tgz",
"integrity": "sha512-YGJe+QorDGbkWDSUg6x69LYGN62Kgvb92Iz/voqmszVRKj4KcoPvd/7coF8Jmu+ZQE6LcwM/9ccB2i63L99ITA==", "integrity": "sha512-ko7g54YSEcSq2K/GEpmiTG0foGLqe7DwgXGhkGxYEiHhLAUv8WvQmrFsm8e/KOW7n8mLGq0uaZVe2l8m3JTGGQ==",
"requires": { "requires": {
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",
"lodash.keyby": "^4.6.0", "lodash.keyby": "^4.6.0",
@@ -7298,6 +7298,11 @@
"locate-path": "^3.0.0" "locate-path": "^3.0.0"
} }
}, },
"flat": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
"integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ=="
},
"flat-cache": { "flat-cache": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",

View File

@@ -3,7 +3,7 @@
"version": "1.7.1", "version": "1.7.1",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@blueprintjs-formik/core": "^0.2.1", "@blueprintjs-formik/core": "^0.3.3",
"@blueprintjs-formik/datetime": "^0.3.4", "@blueprintjs-formik/datetime": "^0.3.4",
"@blueprintjs-formik/select": "^0.2.3", "@blueprintjs-formik/select": "^0.2.3",
"@blueprintjs/core": "^3.50.2", "@blueprintjs/core": "^3.50.2",
@@ -45,6 +45,7 @@
"deepdash": "^5.3.9", "deepdash": "^5.3.9",
"dependency-graph": "^0.11.0", "dependency-graph": "^0.11.0",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"flat": "^5.0.2",
"formik": "^2.2.5", "formik": "^2.2.5",
"http-proxy-middleware": "^1.0.0", "http-proxy-middleware": "^1.0.0",
"jest": "24.9.0", "jest": "24.9.0",

View File

@@ -46,7 +46,7 @@ const SelectButton = styled(Button)`
margin-right: 12px; margin-right: 12px;
border-radius: 1px; border-radius: 1px;
} }
&:not([class*='bp3-intent-']) { &:not([class*='bp3-intent-']):not(.bp3-disabled) {
&, &,
&:hover { &:hover {
background: #fff; background: #fff;

View File

@@ -30,7 +30,7 @@ export const getDefaultGeneralLedgerQuery = () => {
return { return {
fromDate: moment().startOf('year').format('YYYY-MM-DD'), fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
basis: 'accural', basis: 'accrual',
filterByOption: 'with-transactions', filterByOption: 'with-transactions',
branchesIds: [], branchesIds: [],
accountsIds: [], accountsIds: [],

View File

@@ -13,7 +13,7 @@ export const getDefaultJournalQuery = () => {
return { return {
fromDate: moment().startOf('year').format('YYYY-MM-DD'), fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
basis: 'accural', basis: 'accrual',
}; };
}; };

View File

@@ -27,7 +27,7 @@ export default function RadiosAccountingBasis(props) {
{...rest} {...rest}
> >
<Radio label={intl.get('cash')} value="cash" /> <Radio label={intl.get('cash')} value="cash" />
<Radio label={intl.get('accrual')} value="accural" /> <Radio label={intl.get('accrual')} value="accrual" />
</RadioGroup> </RadioGroup>
)} )}
</FastField> </FastField>

View File

@@ -13,7 +13,7 @@ export function getDefaultTrialBalanceQuery() {
return { return {
fromDate: moment().startOf('year').format('YYYY-MM-DD'), fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
basis: 'accural', basis: 'accrual',
filterByOption: 'with-transactions', filterByOption: 'with-transactions',
branchesIds: [], branchesIds: [],
}; };

View File

@@ -2,12 +2,18 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
const Schema = Yup.object().shape({ const Schema = Yup.object().shape({
accounting_basis: Yup.string().required(), organization: Yup.object({
account_code_required: Yup.boolean().nullable(), accountingBasis: Yup.string().required(),
account_code_unique: Yup.boolean().nullable(), }),
withdrawal_account: Yup.number().nullable(), accounts: Yup.object({
preferred_deposit_account: Yup.number().nullable(), accountCodeRequired: Yup.boolean().nullable(),
preferred_advance_deposit: Yup.number().nullable(), accountCodeUnique: Yup.boolean().nullable(),
}),
paymentReceives: Yup.object({
preferredDepositAccount: Yup.number().nullable(),
preferredAdvanceDeposit: Yup.number().nullable(),
withdrawalAccount: Yup.number().nullable(),
})
}); });
export const AccountantSchema = Schema; export const AccountantSchema = Schema;

View File

@@ -1,16 +1,9 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Form, FastField, useFormikContext } from 'formik'; import { Form, useFormikContext } from 'formik';
import styled from 'styled-components'; import styled from 'styled-components';
import { import { FormGroup, Radio, Button, Intent } from '@blueprintjs/core';
FormGroup,
RadioGroup,
Radio,
Checkbox,
Button,
Intent,
} from '@blueprintjs/core';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { import {
@@ -19,8 +12,9 @@ import {
FieldRequiredHint, FieldRequiredHint,
CardFooterActions, CardFooterActions,
FFormGroup, FFormGroup,
FCheckbox,
FRadioGroup,
} from '@/components'; } from '@/components';
import { handleStringChange, inputIntent } from '@/utils';
import { ACCOUNT_PARENT_TYPE, ACCOUNT_TYPE } from '@/constants/accountTypes'; import { ACCOUNT_PARENT_TYPE, ACCOUNT_TYPE } from '@/constants/accountTypes';
import { useAccountantFormContext } from './AccountantFormProvider'; import { useAccountantFormContext } from './AccountantFormProvider';
@@ -30,7 +24,6 @@ import { useAccountantFormContext } from './AccountantFormProvider';
export default function AccountantForm() { export default function AccountantForm() {
const history = useHistory(); const history = useHistory();
const { accounts } = useAccountantFormContext(); const { accounts } = useAccountantFormContext();
const { isSubmitting } = useFormikContext(); const { isSubmitting } = useFormikContext();
const handleCloseClick = () => { const handleCloseClick = () => {
@@ -48,78 +41,54 @@ export default function AccountantForm() {
} }
className={'accounts-checkbox'} className={'accounts-checkbox'}
> >
{/*------------ Account code (required) -----------*/} {/*------------ Account Code (required) -----------*/}
<FastField name={'account_code_required'} type={'checkbox'}> <FFormGroup inline={true} name={'accounts.accountCodeRequired'}>
{({ field }) => ( <FCheckbox
<FormGroup inline={true}> inline={true}
<Checkbox label={
<T id={'make_account_code_required_when_create_a_new_accounts'} />
}
name={'accounts.accountCodeRequired'}
/>
</FFormGroup>
{/*------------ Account Code (unique) -----------*/}
<FFormGroup
name={'accounts.accountCodeUnique'}
type={'checkbox'}
inline={true}
>
<FCheckbox
inline={true} inline={true}
label={ label={
<T <T
id={'make_account_code_required_when_create_a_new_accounts'} id={'should_account_code_be_unique_when_create_a_new_account'}
/> />
} }
name={'account_code_required'} name={'accounts.accountCodeUnique'}
{...field}
/> />
</FormGroup> </FFormGroup>
)}
</FastField>
{/*------------ Account code (unique) -----------*/}
<FastField name={'account_code_unique'} type={'checkbox'}>
{({ field }) => (
<FormGroup inline={true}>
<Checkbox
inline={true}
label={
<T
id={
'should_account_code_be_unique_when_create_a_new_account'
}
/>
}
name={'account_code_unique'}
{...field}
/>
</FormGroup>
)}
</FastField>
</FormGroup> </FormGroup>
{/* ----------- Accounting basis ----------- */} {/* ----------- Accounting Basis ----------- */}
<FastField name={'accounting_basis'}>
{({
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup <FormGroup
name={'organization.accountingBasis'}
labelInfo={<FieldRequiredHint />} labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
label={ label={
<strong> <strong>
<T id={'accounting_basis_'} /> <T id={'accounting_basis_'} />
</strong> </strong>
} }
> >
<RadioGroup <FRadioGroup name={'organization.accountingBasis'} inline={true}>
inline={true}
selectedValue={value}
onChange={handleStringChange((_value) => {
setFieldValue('accounting_basis', _value);
})}
>
<Radio label={intl.get('cash')} value="cash" /> <Radio label={intl.get('cash')} value="cash" />
<Radio label={intl.get('accrual')} value="accrual" /> <Radio label={intl.get('accrual')} value="accrual" />
</RadioGroup> </FRadioGroup>
</FormGroup> </FormGroup>
)}
</FastField>
{/* ----------- Deposit customer account ----------- */} {/* ----------- Deposit Customer Account ----------- */}
<AccountantFormGroup <AccountantFormGroup
name={'preferred_deposit_account'} name={'paymentReceives.preferredDepositAccount'}
label={ label={
<strong> <strong>
<T id={'deposit_customer_account'} /> <T id={'deposit_customer_account'} />
@@ -136,7 +105,7 @@ export default function AccountantForm() {
fastField={true} fastField={true}
> >
<AccountsSelect <AccountsSelect
name={'preferred_deposit_account'} name={'paymentReceives.preferredDepositAccount'}
items={accounts} items={accounts}
placeholder={<T id={'select_payment_account'} />} placeholder={<T id={'select_payment_account'} />}
filterByTypes={[ filterByTypes={[
@@ -148,9 +117,9 @@ export default function AccountantForm() {
/> />
</AccountantFormGroup> </AccountantFormGroup>
{/* ----------- Withdrawal vendor account ----------- */} {/* ----------- Withdrawal Vendor Account ----------- */}
<AccountantFormGroup <AccountantFormGroup
name={'withdrawal_account'} name={'billPayments.withdrawalAccount'}
label={ label={
<strong> <strong>
<T id={'withdrawal_vendor_account'} /> <T id={'withdrawal_vendor_account'} />
@@ -167,7 +136,7 @@ export default function AccountantForm() {
fastField={true} fastField={true}
> >
<AccountsSelect <AccountsSelect
name={'withdrawal_account'} name={'billPayments.withdrawalAccount'}
items={accounts} items={accounts}
placeholder={<T id={'select_payment_account'} />} placeholder={<T id={'select_payment_account'} />}
filterByTypes={[ filterByTypes={[
@@ -179,9 +148,9 @@ export default function AccountantForm() {
/> />
</AccountantFormGroup> </AccountantFormGroup>
{/* ----------- Withdrawal customer account ----------- */} {/* ----------- Withdrawal Customer Account ----------- */}
<AccountantFormGroup <AccountantFormGroup
name={'preferred_advance_deposit'} name={'paymentReceives.preferredAdvanceDeposit'}
label={ label={
<strong> <strong>
<T id={'customer_advance_deposit'} /> <T id={'customer_advance_deposit'} />
@@ -198,7 +167,7 @@ export default function AccountantForm() {
fastField={true} fastField={true}
> >
<AccountsSelect <AccountsSelect
name={'preferred_advance_deposit'} name={'paymentReceives.preferredAdvanceDeposit'}
items={accounts} items={accounts}
placeholder={<T id={'select_payment_account'} />} placeholder={<T id={'select_payment_account'} />}
filterByParentTypes={[ACCOUNT_PARENT_TYPE.CURRENT_ASSET]} filterByParentTypes={[ACCOUNT_PARENT_TYPE.CURRENT_ASSET]}

View File

@@ -1,55 +1,66 @@
// @ts-nocheck // @ts-nocheck
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import * as R from 'ramda';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Formik } from 'formik'; import { Formik } from 'formik';
import { pick } from 'lodash';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { flatten, unflatten } from 'flat';
import { AppToaster } from '@/components'; import { AppToaster } from '@/components';
import withDashboardActions from '@/containers/Dashboard/withDashboardActions'; import withDashboardActions from '@/containers/Dashboard/withDashboardActions';
import withSettings from '@/containers/Settings/withSettings'; import withSettings from '@/containers/Settings/withSettings';
import AccountantForm from './AccountantForm'; import AccountantForm from './AccountantForm';
import { AccountantSchema } from './Accountant.schema'; import { AccountantSchema } from './Accountant.schema';
import { useAccountantFormContext } from './AccountantFormProvider'; import { useAccountantFormContext } from './AccountantFormProvider';
import { transformToOptions } from './utils'; import { transferObjectOptionsToArray } from './utils';
import { compose, transformGeneralSettings } from '@/utils'; import { compose, transformToForm, transfromToSnakeCase } from '@/utils';
import '@/style/pages/Preferences/Accounting.scss'; import '@/style/pages/Preferences/Accounting.scss';
const defaultFormValues = flatten({
organization: {
accountingBasis: 'accrual',
},
accounts: {
accountCodeRequired: false,
accountCodeUnique: false,
},
billPayments: {
withdrawalAccount: '',
},
paymentReceives: {
preferredDepositAccount: '',
preferredAdvanceDeposit: '',
},
});
// Accountant preferences. // Accountant preferences.
function AccountantFormPage({ function AccountantFormPage({
//# withDashboardActions //# withDashboardActions
changePreferencesPageTitle, changePreferencesPageTitle,
// #withSettings // #withSettings
organizationSettings, allSettings,
paymentReceiveSettings,
accountsSettings,
billPaymentSettings,
}) { }) {
const { saveSettingMutate } = useAccountantFormContext(); const { saveSettingMutate } = useAccountantFormContext();
const accountantSettings = {
...billPaymentSettings,
...accountsSettings,
...pick(organizationSettings, ['accountingBasis']),
...pick(paymentReceiveSettings, ['preferredDepositAccount', 'preferredAdvanceDeposit']),
};
const initialValues = {
...transformGeneralSettings(accountantSettings),
};
useEffect(() => { useEffect(() => {
changePreferencesPageTitle(intl.get('accountant')); changePreferencesPageTitle(intl.get('accountant'));
}, [changePreferencesPageTitle]); }, [changePreferencesPageTitle]);
const initialValues = unflatten({
...defaultFormValues,
...transformToForm(flatten(allSettings), defaultFormValues),
});
// Handle the form submitting.
const handleFormSubmit = (values, { setSubmitting }) => { const handleFormSubmit = (values, { setSubmitting }) => {
const options = transformToOptions(values); const options = R.compose(
transferObjectOptionsToArray,
transfromToSnakeCase,
)(values);
setSubmitting(true); setSubmitting(true);
const onSuccess = () => { const onSuccess = () => {
AppToaster.show({ AppToaster.show({
message: intl.get('the_accountant_preferences_has_been_saved'), message: intl.get('the_accountant_preferences_has_been_saved'),
@@ -57,8 +68,7 @@ function AccountantFormPage({
}); });
setSubmitting(false); setSubmitting(false);
}; };
const onError = () => {
const onError = (errors) => {
setSubmitting(false); setSubmitting(false);
}; };
saveSettingMutate({ options }).then(onSuccess).catch(onError); saveSettingMutate({ options }).then(onSuccess).catch(onError);
@@ -75,18 +85,8 @@ function AccountantFormPage({
} }
export default compose( export default compose(
withSettings( withSettings(({ allSettings }) => ({
({ allSettings,
organizationSettings, })),
paymentReceiveSettings,
accountsSettings,
billPaymentSettings,
}) => ({
organizationSettings,
paymentReceiveSettings,
accountsSettings,
billPaymentSettings,
}),
),
withDashboardActions, withDashboardActions,
)(AccountantFormPage); )(AccountantFormPage);

View File

@@ -17,7 +17,7 @@ function AccountantFormProvider({ ...props }) {
// Fetches the accounts list. // Fetches the accounts list.
const { isLoading: isAccountsLoading, data: accounts } = useAccounts(); const { isLoading: isAccountsLoading, data: accounts } = useAccounts();
//Fetches Organization Settings. // Fetches Organization Settings.
const { isLoading: isSettingsLoading } = useSettings(); const { isLoading: isSettingsLoading } = useSettings();
// Save Organization Settings. // Save Organization Settings.
@@ -29,7 +29,7 @@ function AccountantFormProvider({ ...props }) {
isAccountsLoading, isAccountsLoading,
saveSettingMutate, saveSettingMutate,
}; };
// Detarmines whether if any query is loading.
const isLoading = isSettingsLoading || isAccountsLoading; const isLoading = isSettingsLoading || isAccountsLoading;
return ( return (

View File

@@ -1,38 +1,6 @@
// @ts-nocheck // @ts-nocheck
export const transformToOptions = (option) => {
return [
{
key: 'accounting_basis',
value: option.accounting_basis,
group: 'organization',
},
{
key: 'withdrawal_account',
value: option.withdrawal_account,
group: 'bill_payments',
},
{
key: 'preferred_deposit_account',
value: option.preferred_deposit_account,
group: 'payment_receives',
},
{
key: 'preferred_advance_deposit',
value: option.preferred_advance_deposit,
group: 'payment_receives',
},
{
key: 'account_code_required',
value: option.account_code_required,
group: 'accounts',
},
{
key: 'account_code_unique',
value: option.account_code_unique,
group: 'accounts',
},
];
};
export const transferObjectOptionsToArray = (input) =>
Object.entries(input).flatMap(([group, options]) =>
Object.entries(options).map(([key, value]) => ({ group, key, value })),
);

View File

@@ -10,6 +10,7 @@ import { ActionMenuList, useCurrenciesTableColumns } from './components';
import withDialogActions from '@/containers/Dialog/withDialogActions'; import withDialogActions from '@/containers/Dialog/withDialogActions';
import withAlertActions from '@/containers/Alert/withAlertActions'; import withAlertActions from '@/containers/Alert/withAlertActions';
import styled from 'styled-components';
/** /**
* Currencies table. * Currencies table.
@@ -46,7 +47,7 @@ function CurrenciesDataTable({
}; };
return ( return (
<DataTable <CurrencieDataTable
columns={columns} columns={columns}
data={currencies} data={currencies}
loading={isCurrenciesLoading} loading={isCurrenciesLoading}
@@ -68,3 +69,11 @@ export default compose(
withDialogActions, withDialogActions,
withAlertActions, withAlertActions,
)(CurrenciesDataTable); )(CurrenciesDataTable);
const CurrencieDataTable = styled(DataTable)`
.table .th,
.table .td {
padding-top: 0.4rem;
padding-bottom: 0.4rem;
}
`;

View File

@@ -3,15 +3,17 @@ import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import classNames from 'classnames'; import classNames from 'classnames';
import { Form } from 'formik'; import { Form } from 'formik';
import { Button, FormGroup, InputGroup, Intent } from '@blueprintjs/core'; import { Button, FormGroup, Intent } from '@blueprintjs/core';
import { TimezonePicker } from '@blueprintjs/timezone'; import { TimezonePicker } from '@blueprintjs/timezone';
import { ErrorMessage, FastField } from 'formik'; import { ErrorMessage, FastField } from 'formik';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { import {
ListSelect,
FieldRequiredHint, FieldRequiredHint,
FormattedMessage as T, FormattedMessage as T,
FFormGroup,
FInputGroup,
FSelect,
} from '@/components'; } from '@/components';
import { inputIntent } from '@/utils'; import { inputIntent } from '@/utils';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
@@ -46,153 +48,114 @@ export default function PreferencesGeneralForm({ isSubmitting }) {
return ( return (
<Form> <Form>
{/* ---------- Organization name ---------- */} {/* ---------- Organization name ---------- */}
<FastField name={'name'}> <FFormGroup
{({ field, meta: { error, touched } }) => ( name={'name'}
<FormGroup
label={<T id={'organization_name'} />} label={<T id={'organization_name'} />}
labelInfo={<FieldRequiredHint />} labelInfo={<FieldRequiredHint />}
inline={true} inline={true}
intent={inputIntent({ error, touched })}
className={'form-group--org-name'}
helperText={<T id={'shown_on_sales_forms_and_purchase_orders'} />} helperText={<T id={'shown_on_sales_forms_and_purchase_orders'} />}
fastField={true}
> >
<InputGroup medium={'true'} {...field} /> <FInputGroup medium={'true'} name={'name'} fastField={true} />
</FormGroup> </FFormGroup>
)}
</FastField>
{/* ---------- Industry ---------- */} {/* ---------- Industry ---------- */}
<FastField name={'industry'}> <FFormGroup
{({ field, meta: { error, touched } }) => ( name={'industry'}
<FormGroup
label={<T id={'organization_industry'} />} label={<T id={'organization_industry'} />}
inline={true} inline={true}
intent={inputIntent({ error, touched })} fastField={true}
helperText={<ErrorMessage name="industry" />}
className={'form-group--org-industry'}
> >
<InputGroup medium={'true'} {...field} /> <FInputGroup name={'industry'} medium={'true'} fastField={true} />
</FormGroup> </FFormGroup>
)}
</FastField>
{/* ---------- Location ---------- */} {/* ---------- Location ---------- */}
<FastField name={'location'}> <FFormGroup
{({ form, field: { value }, meta: { error, touched } }) => ( name={'location'}
<FormGroup
label={<T id={'business_location'} />} label={<T id={'business_location'} />}
className={classNames(
'form-group--business-location',
CLASSES.FILL,
)}
inline={true} inline={true}
helperText={<ErrorMessage name="location" />} fastField={true}
intent={inputIntent({ error, touched })}
> >
<ListSelect <FSelect
name={'location'}
items={Countries} items={Countries}
onItemSelect={({ value }) => { valueAccessor={'countryCode'}
form.setFieldValue('location', value); labelAccessor={'countryCode'}
}} textAccessor={'name'}
selectedItem={value} placeholder={<T id={'select_business_location'} />}
selectedItemProp={'value'}
defaultText={<T id={'select_business_location'} />}
textProp={'name'}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
fastField={true}
/> />
</FormGroup> </FFormGroup>
)}
</FastField>
{/* ---------- Base currency ---------- */} {/* ---------- Base currency ---------- */}
<FastField <FFormGroup
name={'base_currency'} name={'base_currency'}
baseCurrencyDisabled={baseCurrencyDisabled} baseCurrencyDisabled={baseCurrencyDisabled}
shouldUpdate={shouldBaseCurrencyUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'base_currency'} />} label={<T id={'base_currency'} />}
labelInfo={<FieldRequiredHint />} labelInfo={<FieldRequiredHint />}
className={classNames('form-group--base-currency', CLASSES.FILL)}
inline={true} inline={true}
intent={inputIntent({ error, touched })}
helperText={ helperText={
<T <T
id={ id={'you_can_t_change_the_base_currency_as_there_are_transactions'}
'you_can_t_change_the_base_currency_as_there_are_transactions'
}
/> />
} }
fastField={true}
shouldUpdate={shouldBaseCurrencyUpdate}
> >
<ListSelect <FSelect
name={'base_currency'}
items={Currencies} items={Currencies}
onItemSelect={(currency) => { valueAccessor={'key'}
form.setFieldValue('base_currency', currency.key); textAccessor={'name'}
}} labelAccessor={'key'}
selectedItem={value} placeholder={<T id={'select_base_currency'} />}
selectedItemProp={'key'}
defaultText={<T id={'select_base_currency'} />}
textProp={'name'}
labelProp={'key'}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
disabled={baseCurrencyDisabled} disabled={baseCurrencyDisabled}
fastField={true}
shouldUpdate={shouldBaseCurrencyUpdate}
baseCurrencyDisabled={baseCurrencyDisabled}
/> />
</FormGroup> </FFormGroup>
)}
</FastField>
{/* --------- Fiscal Year ----------- */} {/* --------- Fiscal Year ----------- */}
<FastField name={'fiscal_year'}> <FFormGroup
{({ form, field: { value }, meta: { error, touched } }) => ( name={'fiscal_year'}
<FormGroup
label={<T id={'fiscal_year'} />} label={<T id={'fiscal_year'} />}
labelInfo={<FieldRequiredHint />} labelInfo={<FieldRequiredHint />}
className={classNames('form-group--fiscal-year', CLASSES.FILL)}
inline={true} inline={true}
intent={inputIntent({ error, touched })}
helperText={<T id={'for_reporting_you_can_specify_any_month'} />} helperText={<T id={'for_reporting_you_can_specify_any_month'} />}
fastField={true}
> >
<ListSelect <FSelect
name={'fiscal_year'}
items={FiscalYear} items={FiscalYear}
onItemSelect={(option) => { valueAccessor={'key'}
form.setFieldValue('fiscal_year', option.key); textAccessor={'name'}
}} placeholder={<T id={'select_fiscal_year'} />}
selectedItem={value}
selectedItemProp={'key'}
defaultText={<T id={'select_fiscal_year'} />}
textProp={'name'}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
fastField={true}
/> />
</FormGroup> </FFormGroup>
)}
</FastField>
{/* ---------- Language ---------- */} {/* ---------- Language ---------- */}
<FastField name={'language'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup <FormGroup
name={'language'}
label={<T id={'language'} />} label={<T id={'language'} />}
labelInfo={<FieldRequiredHint />} labelInfo={<FieldRequiredHint />}
inline={true} inline={true}
className={classNames('form-group--language', CLASSES.FILL)} fastField={true}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="language" />}
> >
<ListSelect <FSelect
name={'language'}
items={Languages} items={Languages}
selectedItemProp={'value'} valueAccessor={'value'}
textProp={'name'} textAccessor={'name'}
defaultText={<T id={'select_language'} />} placeholder={<T id={'select_language'} />}
selectedItem={value}
onItemSelect={(item) =>
form.setFieldValue('language', item.value)
}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
fastField={true}
/> />
</FormGroup> </FormGroup>
)}
</FastField>
{/* ---------- Time zone ---------- */} {/* ---------- Time zone ---------- */}
<FastField name={'timezone'}> <FastField name={'timezone'}>
@@ -222,30 +185,24 @@ export default function PreferencesGeneralForm({ isSubmitting }) {
</FastField> </FastField>
{/* --------- Data format ----------- */} {/* --------- Data format ----------- */}
<FastField name={'date_format'}> <FFormGroup
{({ form, field: { value }, meta: { error, touched } }) => ( name={'date_format'}
<FormGroup
label={<T id={'date_format'} />} label={<T id={'date_format'} />}
labelInfo={<FieldRequiredHint />} labelInfo={<FieldRequiredHint />}
inline={true} inline={true}
className={classNames('form-group--date-format', CLASSES.FILL)}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="date_format" />} helperText={<ErrorMessage name="date_format" />}
fastField={true}
> >
<ListSelect <FSelect
name={'date_format'}
items={dateFormats} items={dateFormats}
onItemSelect={(dateFormat) => { valueAccessor={'key'}
form.setFieldValue('date_format', dateFormat.key); textAccessor={'label'}
}} placeholder={<T id={'select_date_format'} />}
selectedItem={value}
selectedItemProp={'key'}
defaultText={<T id={'select_date_format'} />}
textProp={'label'}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
fastField={true}
/> />
</FormGroup> </FFormGroup>
)}
</FastField>
<CardFooterActions> <CardFooterActions>
<Button loading={isSubmitting} intent={Intent.PRIMARY} type="submit"> <Button loading={isSubmitting} intent={Intent.PRIMARY} type="submit">

View File

@@ -40,6 +40,7 @@ function GeneralFormPage({
// Initial values. // Initial values.
const initialValues = { const initialValues = {
...defaultValues,
...transformToForm(organization.metadata, defaultValues), ...transformToForm(organization.metadata, defaultValues),
}; };
// Handle the form submit. // Handle the form submit.

View File

@@ -23,6 +23,7 @@ function GeneralFormProvider({ ...props }) {
const { isLoading: isOrganizationLoading, data: organization } = const { isLoading: isOrganizationLoading, data: organization } =
useCurrentOrganization(); useCurrentOrganization();
// Fetch date format options.
const { data: dateFormats, isLoading: isDateFormatsLoading } = const { data: dateFormats, isLoading: isDateFormatsLoading } =
useDateFormats(); useDateFormats();

View File

@@ -1,8 +1,9 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import { Form, useFormikContext } from 'formik'; import { Form, useFormikContext } from 'formik';
import { FormGroup, Button, Intent } from '@blueprintjs/core'; import { Button, Intent } from '@blueprintjs/core';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import styled from 'styled-components';
import { import {
AccountsSelect, AccountsSelect,
FieldRequiredHint, FieldRequiredHint,
@@ -30,7 +31,7 @@ export default function ItemForm() {
return ( return (
<Form> <Form>
{/* ----------- Preferred Sell Account ----------- */} {/* ----------- Preferred Sell Account ----------- */}
<FormGroup <ItemFormGroup
name={'preferred_sell_account'} name={'preferred_sell_account'}
label={ label={
<strong> <strong>
@@ -53,10 +54,10 @@ export default function ItemForm() {
placeholder={<T id={'select_payment_account'} />} placeholder={<T id={'select_payment_account'} />}
filterByParentTypes={[ACCOUNT_PARENT_TYPE.INCOME]} filterByParentTypes={[ACCOUNT_PARENT_TYPE.INCOME]}
/> />
</FormGroup> </ItemFormGroup>
{/* ----------- Preferred Cost Account ----------- */} {/* ----------- Preferred Cost Account ----------- */}
<FFormGroup <ItemFormGroup
name={'preferred_cost_account'} name={'preferred_cost_account'}
label={ label={
<strong> <strong>
@@ -79,10 +80,10 @@ export default function ItemForm() {
placeholder={<T id={'select_payment_account'} />} placeholder={<T id={'select_payment_account'} />}
filterByParentTypes={[ACCOUNT_PARENT_TYPE.EXPENSE]} filterByParentTypes={[ACCOUNT_PARENT_TYPE.EXPENSE]}
/> />
</FFormGroup> </ItemFormGroup>
{/* ----------- Preferred Inventory Account ----------- */} {/* ----------- Preferred Inventory Account ----------- */}
<FFormGroup <ItemFormGroup
name={'preferred_inventory_account'} name={'preferred_inventory_account'}
label={ label={
<strong> <strong>
@@ -105,7 +106,7 @@ export default function ItemForm() {
placeholder={<T id={'select_payment_account'} />} placeholder={<T id={'select_payment_account'} />}
filterByTypes={[ACCOUNT_TYPE.INVENTORY]} filterByTypes={[ACCOUNT_TYPE.INVENTORY]}
/> />
</FFormGroup> </ItemFormGroup>
<CardFooterActions> <CardFooterActions>
<Button intent={Intent.PRIMARY} loading={isSubmitting} type="submit"> <Button intent={Intent.PRIMARY} loading={isSubmitting} type="submit">
@@ -118,3 +119,7 @@ export default function ItemForm() {
</Form> </Form>
); );
} }
const ItemFormGroup = styled(FFormGroup)`
max-width: 400px;
`;

View File

@@ -11,10 +11,21 @@ import ItemPreferencesForm from './ItemPreferencesForm';
import { useItemPreferencesFormContext } from './ItemPreferencesFormProvider'; import { useItemPreferencesFormContext } from './ItemPreferencesFormProvider';
import withDashboardActions from '@/containers/Dashboard/withDashboardActions'; import withDashboardActions from '@/containers/Dashboard/withDashboardActions';
import withSettings from '@/containers/Settings/withSettings'; import withSettings from '@/containers/Settings/withSettings';
import { compose, optionsMapToArray, transformGeneralSettings } from '@/utils'; import {
compose,
optionsMapToArray,
transformGeneralSettings,
transformToForm,
} from '@/utils';
import '@/style/pages/Preferences/Accounting.scss'; import '@/style/pages/Preferences/Accounting.scss';
const defaultFormValues = {
preferred_sell_account: '',
preferred_cost_account: '',
preferred_inventory_account: '',
};
// item form page preferences. // item form page preferences.
function ItemPreferencesFormPage({ function ItemPreferencesFormPage({
// #withSettings // #withSettings
@@ -25,16 +36,13 @@ function ItemPreferencesFormPage({
}) { }) {
const { saveSettingMutate } = useItemPreferencesFormContext(); const { saveSettingMutate } = useItemPreferencesFormContext();
const itemPerferencesSettings = {
...omit(itemsSettings, ['tableSize']),
};
// Initial values. // Initial values.
const initialValues = { const initialValues = {
preferred_sell_account: '', ...defaultFormValues,
preferred_cost_account: '', ...transformToForm(
preferred_inventory_account: '', transformGeneralSettings(itemsSettings),
...transformGeneralSettings(itemPerferencesSettings), defaultFormValues,
),
}; };
useEffect(() => { useEffect(() => {

View File

@@ -4,6 +4,7 @@ import { connect } from 'react-redux';
export default (mapState) => { export default (mapState) => {
const mapStateToProps = (state, props) => { const mapStateToProps = (state, props) => {
const mapped = { const mapped = {
allSettings: state.settings.data,
organizationSettings: state.settings.data.organization, organizationSettings: state.settings.data.organization,
manualJournalsSettings: state.settings.data.manualJournals, manualJournalsSettings: state.settings.data.manualJournals,
billPaymentSettings: state.settings.data.billPayments, billPaymentSettings: state.settings.data.billPayments,

View File

@@ -1,18 +1,17 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import { FastField, Form, ErrorMessage } from 'formik'; import { FastField, Form, ErrorMessage } from 'formik';
import { import { Button, Intent, FormGroup, Classes } from '@blueprintjs/core';
Button,
Intent,
FormGroup,
MenuItem,
Classes,
} from '@blueprintjs/core';
import classNames from 'classnames'; import classNames from 'classnames';
import { TimezonePicker } from '@blueprintjs/timezone'; import { TimezonePicker } from '@blueprintjs/timezone';
import { FFormGroup, FInputGroup, FormattedMessage as T } from '@/components'; import {
FFormGroup,
FInputGroup,
FSelect,
FormattedMessage as T,
} from '@/components';
import { Col, Row, ListSelect } from '@/components'; import { Col, Row } from '@/components';
import { inputIntent } from '@/utils'; import { inputIntent } from '@/utils';
import { getFiscalYear } from '@/constants/fiscalYearOptions'; import { getFiscalYear } from '@/constants/fiscalYearOptions';
@@ -35,152 +34,88 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
<h3> <h3>
<T id={'organization_details'} /> <T id={'organization_details'} />
</h3> </h3>
{/* ---------- Organization name ---------- */} {/* ---------- Organization name ---------- */}
<FFormGroup name={'name'} label={<T id={'legal_organization_name'} />}> <FFormGroup
<FInputGroup name={'name'} /> name={'name'}
label={<T id={'legal_organization_name'} />}
fastField={true}
>
<FInputGroup name={'name'} fastField={true} />
</FFormGroup> </FFormGroup>
{/* ---------- Location ---------- */} {/* ---------- Location ---------- */}
<FastField name={'location'}> <FFormGroup
{({ form, field: { value }, meta: { error, touched } }) => ( name={'location'}
<FormGroup
label={<T id={'business_location'} />} label={<T id={'business_location'} />}
className={classNames( fastField={true}
'form-group--business-location',
Classes.FILL,
)}
helperText={<ErrorMessage name="location" />}
intent={inputIntent({ error, touched })}
> >
<ListSelect <FSelect
name={'location'}
items={countries} items={countries}
onItemSelect={({ countryCode }) => { valueAccessor={'countryCode'}
form.setFieldValue('location', countryCode); textAccessor={'name'}
}} placeholder={<T id={'select_business_location'} />}
selectedItem={value}
selectedItemProp={'countryCode'}
defaultText={<T id={'select_business_location'} />}
textProp={'name'}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
fastField={true}
/> />
</FormGroup> </FFormGroup>
)}
</FastField>
<Row> <Row>
<Col xs={6}> <Col xs={6}>
{/* ---------- Base currency ---------- */} {/* ---------- Base currency ---------- */}
<FastField name={'baseCurrency'}> <FFormGroup
{({ name={'baseCurrency'}
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'base_currency'} />} label={<T id={'base_currency'} />}
className={classNames( fastField={true}
'form-group--base-currency',
'form-group--select-list',
Classes.FILL,
)}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'baseCurrency'} />}
> >
<ListSelect <FSelect
name={'baseCurrency'}
items={currencies} items={currencies}
noResults={
<MenuItem disabled={true} text={<T id={'no_results'} />} />
}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
onItemSelect={(item) => { valueAccessor={'key'}
setFieldValue('baseCurrency', item.key); textAccessor={'name'}
}} placeholder={<T id={'select_base_currency'} />}
selectedItemProp={'key'} fastField={true}
textProp={'name'}
defaultText={<T id={'select_base_currency'} />}
selectedItem={value}
intent={inputIntent({ error, touched })}
/> />
</FormGroup> </FFormGroup>
)}
</FastField>
</Col> </Col>
{/* ---------- Language ---------- */} {/* ---------- Language ---------- */}
<Col xs={6}> <Col xs={6}>
<FastField name={'language'}> <FFormGroup
{({ name={'language'}
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'language'} />} label={<T id={'language'} />}
className={classNames( fastField={true}
'form-group--language',
'form-group--select-list',
Classes.FILL,
)}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'language'} />}
> >
<ListSelect <FSelect
name={'language'}
items={Languages} items={Languages}
noResults={ valueAccessor={'value'}
<MenuItem disabled={true} text={<T id={'no_results'} />} /> textAccessor={'name'}
} placeholder={<T id={'select_language'} />}
onItemSelect={(item) => {
setFieldValue('language', item.value);
}}
selectedItem={value}
textProp={'name'}
selectedItemProp={'value'}
defaultText={<T id={'select_language'} />}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
filterable={false} fastField={true}
intent={inputIntent({ error, touched })}
/> />
</FormGroup> </FFormGroup>
)}
</FastField>
</Col> </Col>
</Row> </Row>
{/* --------- Fiscal Year ----------- */} {/* --------- Fiscal Year ----------- */}
<FastField name={'fiscalYear'}> <FFormGroup
{({ name={'fiscalYear'}
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'fiscal_year'} />} label={<T id={'fiscal_year'} />}
className={classNames( fastField={true}
'form-group--fiscal_year',
'form-group--select-list',
Classes.FILL,
)}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'fiscalYear'} />}
> >
<ListSelect <FSelect
name={'fiscalYear'}
items={FiscalYear} items={FiscalYear}
noResults={ valueAccessor={'key'}
<MenuItem disabled={true} text={<T id={'no_results'} />} /> textAccessor={'name'}
} placeholder={<T id={'select_fiscal_year'} />}
selectedItem={value}
selectedItemProp={'key'}
textProp={'name'}
defaultText={<T id={'select_fiscal_year'} />}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
onItemSelect={(item) => { fastField={true}
setFieldValue('fiscalYear', item.key);
}}
filterable={false}
/> />
</FormGroup> </FFormGroup>
)}
</FastField>
{/* ---------- Time zone ---------- */} {/* ---------- Time zone ---------- */}
<FastField name={'timezone'}> <FastField name={'timezone'}>