diff --git a/client/src/config/preferencesMenu.js b/client/src/config/preferencesMenu.js index d2af17598..019555d4a 100644 --- a/client/src/config/preferencesMenu.js +++ b/client/src/config/preferencesMenu.js @@ -21,4 +21,9 @@ export default [ disabled: false, href: '/preferences/accountant', }, + { + text: , + disabled: false, + href: '/preferences/items', + }, ]; diff --git a/client/src/containers/Preferences/Item/Item.js b/client/src/containers/Preferences/Item/Item.js new file mode 100644 index 000000000..bc2e888ae --- /dev/null +++ b/client/src/containers/Preferences/Item/Item.js @@ -0,0 +1,14 @@ +import React from 'react'; +import ItemFormPage from './ItemFormPage'; +import { ItemFormProvider } from './ItemFormProvider'; + +/** + * items preferences. + */ +export default function ItemsPreferences() { + return ( + + + + ); +} diff --git a/client/src/containers/Preferences/Item/Item.schema.js b/client/src/containers/Preferences/Item/Item.schema.js new file mode 100644 index 000000000..ccb5698f2 --- /dev/null +++ b/client/src/containers/Preferences/Item/Item.schema.js @@ -0,0 +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(), +}); + +export const ItemPreferencesSchema = Schema; diff --git a/client/src/containers/Preferences/Item/ItemForm.js b/client/src/containers/Preferences/Item/ItemForm.js new file mode 100644 index 000000000..5ac6cf265 --- /dev/null +++ b/client/src/containers/Preferences/Item/ItemForm.js @@ -0,0 +1,133 @@ +import React from 'react'; +import { Form, FastField, useFormikContext } from 'formik'; +import { FormGroup, Button, Intent } from '@blueprintjs/core'; +import { AccountsSelectList, FieldRequiredHint } from 'components'; +import { FormattedMessage as T, useIntl } from 'react-intl'; +import { useHistory } from 'react-router-dom'; +import { inputIntent } from 'utils'; +import { ACCOUNT_PARENT_TYPE, ACCOUNT_TYPE } from 'common/accountTypes'; + +import { useItemFormContext } from './ItemFormProvider'; + +/** + * item form preferences. + */ +export default function ItemForm() { + const history = useHistory(); + const { accounts } = useItemFormContext(); + + const { isSubmitting } = useFormikContext(); + + const handleCloseClick = () => { + history.go(-1); + }; + + return ( +
+ {/* ----------- preferred sell account ----------- */} + + {({ + form: { values, setFieldValue }, + field: { value }, + meta: { error, touched }, + }) => ( + + + + } + helperText={ + 'Select a preferred account to deposit into it after customer make payment.' + } + labelInfo={} + intent={inputIntent({ error, touched })} + > + { + setFieldValue('sell_account', id); + }} + selectedAccountId={value} + defaultSelectText={} + filterByParentTypes={[ACCOUNT_PARENT_TYPE.INCOME]} + /> + + )} + + + {/* ----------- preferred cost account ----------- */} + + {({ + form: { values, setFieldValue }, + field: { value }, + meta: { error, touched }, + }) => ( + + + + } + helperText={ + 'Select a preferred account to deposit into it after customer make payment.' + } + labelInfo={} + intent={inputIntent({ error, touched })} + > + { + setFieldValue('cost_account', id); + }} + selectedAccountId={value} + defaultSelectText={} + filterByParentTypes={[ACCOUNT_PARENT_TYPE.EXPENSE]} + /> + + )} + + + {/* ----------- preferred inventory account ----------- */} + + {({ + form: { values, setFieldValue }, + field: { value }, + meta: { error, touched }, + }) => ( + + + + } + helperText={ + 'Select a preferred account to deposit into it vendor advanced deposits.' + } + labelInfo={} + intent={inputIntent({ error, touched })} + > + { + setFieldValue('inventory_account', id); + }} + selectedAccountId={value} + defaultSelectText={} + filterByTypes={[ACCOUNT_TYPE.INVENTORY]} + /> + + )} + + +
+ + +
+
+ ); +} diff --git a/client/src/containers/Preferences/Item/ItemFormPage.js b/client/src/containers/Preferences/Item/ItemFormPage.js new file mode 100644 index 000000000..e9bbdcfc2 --- /dev/null +++ b/client/src/containers/Preferences/Item/ItemFormPage.js @@ -0,0 +1,79 @@ +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'; +import ItemForm from './ItemForm'; + +import { useItemFormContext } from './ItemFormProvider'; +import withDashboardActions from 'containers/Dashboard/withDashboardActions'; +import withSettings from 'containers/Settings/withSettings'; +import { compose, optionsMapToArray, transformGeneralSettings } from 'utils'; + +import 'style/pages/Preferences/Accounting.scss'; + +// item form page preferences. +function ItemFormPage({ + // #withSettings + itemsSettings, + //# withDashboardActions + changePreferencesPageTitle, +}) { + const { formatMessage } = useIntl(); + const { saveSettingMutate } = useItemFormContext(); + + const initialValues = { + ...transformGeneralSettings(itemsSettings), + }; + + useEffect(() => { + changePreferencesPageTitle(formatMessage({ id: 'items' })); + }, [changePreferencesPageTitle]); + + const handleFormSubmit = (values, { setSubmitting, setErrors }) => { + const options = optionsMapToArray(values).map((option) => { + return { key: option.key, ...option, group: 'items' }; + }); + + const onSuccess = () => { + AppToaster.show({ + message: formatMessage({ + id: 'the_items_preferences_has_been_saved', + }), + intent: Intent.SUCCESS, + }); + setSubmitting(false); + }; + + const onError = (errors) => { + setSubmitting(false); + }; + saveSettingMutate({ options }).then(onSuccess).catch(onError); + }; + + return ( +
+
+ +
+
+ ); +} + +export default compose( + withSettings(({ itemsSettings }) => ({ itemsSettings })), + withDashboardActions, +)(ItemFormPage); diff --git a/client/src/containers/Preferences/Item/ItemFormProvider.js b/client/src/containers/Preferences/Item/ItemFormProvider.js new file mode 100644 index 000000000..a70f27c1a --- /dev/null +++ b/client/src/containers/Preferences/Item/ItemFormProvider.js @@ -0,0 +1,34 @@ +import React, { useContext, createContext } from 'react'; +import { LoadingIndicator } from 'components'; + +import { useAccounts, useSaveSettings } from 'hooks/query'; + +const ItemFormContext = createContext(); + +/** + * Item data provider. + */ + +function ItemFormProvider({ ...props }) { + // Fetches the accounts list. + const { isLoading: isAccountsLoading, data: accounts } = useAccounts(); + + // Save Organization Settings. + const { mutateAsync: saveSettingMutate } = useSaveSettings(); + + // Provider state. + const provider = { + accounts, + saveSettingMutate, + }; + + return ( + + + + ); +} + +const useItemFormContext = () => useContext(ItemFormContext); + +export { useItemFormContext, ItemFormProvider }; diff --git a/client/src/containers/Settings/withSettings.js b/client/src/containers/Settings/withSettings.js index d775e9c43..04f104343 100644 --- a/client/src/containers/Settings/withSettings.js +++ b/client/src/containers/Settings/withSettings.js @@ -5,13 +5,14 @@ export default (mapState) => { const mapped = { organizationSettings: state.settings.data.organization, manualJournalsSettings: state.settings.data.manualJournals, - billsettings: state.settings.data.bills, + billPaymentSettings: state.settings.data.billPayments, paymentReceiveSettings: state.settings.data.paymentReceives, estimatesSettings: state.settings.data.salesEstimates, receiptSettings: state.settings.data.salesReceipts, invoiceSettings: state.settings.data.salesInvoices, itemsSettings: state.settings.data.items, expenseSettings: state.settings.data.expenses, + accountsSettings: state.settings.data.accounts, }; return mapState ? mapState(mapped, state, props) : mapped; }; diff --git a/client/src/routes/preferences.js b/client/src/routes/preferences.js index 4a3b88418..e66835ad5 100644 --- a/client/src/routes/preferences.js +++ b/client/src/routes/preferences.js @@ -3,6 +3,7 @@ import Users from 'containers/Preferences/Users/Users'; import Accountant from 'containers/Preferences/Accountant/Accountant'; import Accounts from 'containers/Preferences/Accounts/Accounts'; import Currencies from 'containers/Preferences/Currencies/Currencies'; +import Item from 'containers/Preferences/Item/Item'; const BASE_URL = '/preferences'; @@ -27,4 +28,9 @@ export default [ component: Accountant, exact: true, }, + { + path: `${BASE_URL}/items`, + component: Item, + exact: true, + }, ]; diff --git a/server/src/data/options.js b/server/src/data/options.js index 970742436..f9860b162 100644 --- a/server/src/data/options.js +++ b/server/src/data/options.js @@ -4,7 +4,7 @@ export default { type: "string", }, base_currency: { - type: 'string', + type: "string", }, industry: { type: "string", @@ -13,50 +13,50 @@ export default { type: "string", }, fiscal_year: { - type: 'string', + type: "string", }, financial_date_start: { - type: 'string', + type: "string", }, language: { - type: 'string', + type: "string", }, time_zone: { - type: 'string', + type: "string", }, date_format: { - type: 'string', + type: "string", }, accounting_basis: { - type: 'string', - } + type: "string", + }, }, manual_journals: { next_number: { - type: 'string', + type: "string", }, number_prefix: { - type: 'string', + type: "string", }, auto_increment: { - type: 'boolean', - } + type: "boolean", + }, }, bill_payments: { withdrawal_account: { - type: 'string' + type: "number", }, }, sales_estimates: { next_number: { - type: 'string', + type: "string", }, number_prefix: { - type: 'string', + type: "string", }, auto_increment: { type: "boolean", - } + }, }, sales_receipts: { next_number: { @@ -70,7 +70,7 @@ export default { }, preferred_deposit_account: { type: "number", - } + }, }, sales_invoices: { next_number: { @@ -91,37 +91,37 @@ export default { type: "string", }, auto_increment: { - type: 'boolean', + type: "boolean", }, deposit_account: { - type: 'number', + type: "number", }, advance_deposit: { - type: 'number', - } + type: "number", + }, }, items: { sell_account: { - type: 'number', + type: "number", }, cost_account: { - type: 'number', + type: "number", }, inventory_account: { - type: 'number', + type: "number", }, }, expenses: { preferred_payment_account: { type: "number", - } + }, }, accounts: { account_code_required: { - type: 'boolean', + type: "boolean", }, account_code_unique: { - type: 'boolean', - } - } + type: "boolean", + }, + }, };