+
- {({ isSubmitting}) => (
-
- )}
+
);
}
-export default compose(
- withBillActions,
- withBillDetail(),
- withDashboardActions,
- withMediaActions,
-)(BillForm);
+export default compose(withDashboardActions)(BillForm);
diff --git a/client/src/containers/Purchases/Bill/BillFormBody.js b/client/src/containers/Purchases/Bill/BillFormBody.js
index 2f9845ad7..cef960258 100644
--- a/client/src/containers/Purchases/Bill/BillFormBody.js
+++ b/client/src/containers/Purchases/Bill/BillFormBody.js
@@ -2,11 +2,15 @@ import React from 'react';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import EditableItemsEntriesTable from 'containers/Entries/EditableItemsEntriesTable';
+import { useBillFormContext } from './BillFormProvider';
export default function BillFormBody({ defaultBill }) {
+ const { items } = useBillFormContext();
+
return (
diff --git a/client/src/containers/Purchases/Bill/BillFormFooter.js b/client/src/containers/Purchases/Bill/BillFormFooter.js
index 28ae25381..26bc2e4f2 100644
--- a/client/src/containers/Purchases/Bill/BillFormFooter.js
+++ b/client/src/containers/Purchases/Bill/BillFormFooter.js
@@ -9,10 +9,7 @@ import Dragzone from 'components/Dragzone';
import { inputIntent } from 'utils';
// Bill form floating actions.
-export default function BillFormFooter({
- oninitialFiles,
- onDropFiles,
-}) {
+export default function BillFormFooter() {
return (
+
+
+
);
@@ -277,7 +249,6 @@ export default compose(
withInvoiceActions,
withDashboardActions,
withMediaActions,
- withInvoiceDetail(),
withSettings(({ invoiceSettings }) => ({
invoiceNextNumber: invoiceSettings?.nextNumber,
invoiceNumberPrefix: invoiceSettings?.numberPrefix,
diff --git a/client/src/containers/Sales/Invoice/InvoiceFormHeaderFields.js b/client/src/containers/Sales/Invoice/InvoiceFormHeaderFields.js
index a36c24628..3c38219f8 100644
--- a/client/src/containers/Sales/Invoice/InvoiceFormHeaderFields.js
+++ b/client/src/containers/Sales/Invoice/InvoiceFormHeaderFields.js
@@ -18,21 +18,25 @@ import {
InputPrependButton,
} from 'components';
-import withCustomers from 'containers/Customers/withCustomers';
+import { useInvoiceFormContext } from './InvoiceFormProvider';
+
import withDialogActions from 'containers/Dialog/withDialogActions';
import { inputIntent, handleDateChange } from 'utils';
+/**
+ * Invoice form header fields.
+ */
function InvoiceFormHeaderFields({
- // #withCustomers
- customers,
-
// #withDialogActions
openDialog,
// #ownProps
onInvoiceNumberChanged,
}) {
+ // Invoice form context.
+ const { customers } = useInvoiceFormContext();
+
const handleInvoiceNumberChange = useCallback(() => {
openDialog('invoice-number-form', {});
}, [openDialog]);
@@ -169,8 +173,5 @@ function InvoiceFormHeaderFields({
}
export default compose(
- withCustomers(({ customers }) => ({
- customers,
- })),
withDialogActions,
)(InvoiceFormHeaderFields);
diff --git a/client/src/containers/Sales/Invoice/InvoiceFormPage.js b/client/src/containers/Sales/Invoice/InvoiceFormPage.js
index 9790b262c..4d0819314 100644
--- a/client/src/containers/Sales/Invoice/InvoiceFormPage.js
+++ b/client/src/containers/Sales/Invoice/InvoiceFormPage.js
@@ -1,33 +1,19 @@
import React, { useCallback, useEffect } from 'react';
import { useParams, useHistory } from 'react-router-dom';
-import { useQuery } from 'react-query';
import InvoiceForm from './InvoiceForm';
-import DashboardInsider from 'components/Dashboard/DashboardInsider';
-import withCustomersActions from 'containers/Customers/withCustomersActions';
-import withItemsActions from 'containers/Items/withItemsActions';
-import withInvoiceActions from './withInvoiceActions';
-import withSettingsActions from 'containers/Settings/withSettingsActions';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import { compose } from 'utils';
import 'style/pages/SaleInvoice/PageForm.scss';
+import { InvoiceFormProvider } from './InvoiceFormProvider';
+/**
+ * Invoice form page.
+ */
function InvoiceFormPage({
- // #withCustomersActions
- requestFetchCustomers,
-
- // #withItemsActions
- requestFetchItems,
-
- // #withInvoiceActions
- requsetFetchInvoice,
-
- // #withSettingsActions
- requestFetchOptions,
-
// #withDashboardActions
setSidebarShrink,
resetSidebarPreviousExpand,
@@ -50,54 +36,27 @@ function InvoiceFormPage({
};
}, [resetSidebarPreviousExpand, setSidebarShrink, setDashboardBackLink]);
- const fetchInvoice = useQuery(
- ['invoice', id],
- (key, _id) => requsetFetchInvoice(_id),
- { enabled: !!id },
- );
-
- const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
-
- // Handle fetch Items data table or list
- const fetchItems = useQuery('items-table', () => requestFetchItems({}));
-
const handleFormSubmit = useCallback(
(payload) => {
payload.redirect && history.push('/invoices');
},
[history],
);
- // Handle fetch customers data table or list
- const fetchCustomers = useQuery('customers-table', () =>
- requestFetchCustomers({}),
- );
const handleCancel = useCallback(() => {
history.goBack();
}, [history]);
return (
-
+
-
+
);
}
export default compose(
- withInvoiceActions,
- withCustomersActions,
- withItemsActions,
- withSettingsActions,
withDashboardActions,
)(InvoiceFormPage);
diff --git a/client/src/containers/Sales/Invoice/InvoiceFormProvider.js b/client/src/containers/Sales/Invoice/InvoiceFormProvider.js
new file mode 100644
index 000000000..a8562afc5
--- /dev/null
+++ b/client/src/containers/Sales/Invoice/InvoiceFormProvider.js
@@ -0,0 +1,71 @@
+import React, { createContext, useState } from 'react';
+import DashboardInsider from 'components/Dashboard/DashboardInsider';
+import {
+ useInvoice,
+ useItems,
+ useCustomers,
+ useCreateInvoice,
+ useEditInvoice,
+} from 'hooks/query';
+
+const InvoiceFormContext = createContext();
+
+/**
+ * Accounts chart data provider.
+ */
+function InvoiceFormProvider({ invoiceId, ...props }) {
+ const { data: invoice, isFetching: isInvoiceLoading } = useInvoice(
+ invoiceId,
+ {
+ enabled: !!invoiceId,
+ },
+ );
+
+ // Handle fetching the items table based on the given query.
+ const {
+ data: { items },
+ isFetching: isItemsLoading,
+ } = useItems();
+
+ // Handle fetch customers data table or list
+ const {
+ data: { customers },
+ isFetching: isCustomersLoading,
+ } = useCustomers();
+
+ // Create and edit invoice mutations.
+ const { mutateAsync: createInvoiceMutate } = useCreateInvoice();
+ const { mutateAsync: editInvoiceMutate } = useEditInvoice();
+
+ // Form submit payload.
+ const [submitPayload, setSubmitPayload] = useState({});
+
+ // Provider payload.
+ const provider = {
+ invoice,
+ items,
+ customers,
+ submitPayload,
+
+ isInvoiceLoading,
+ isItemsLoading,
+ isCustomersLoading,
+
+ createInvoiceMutate,
+ editInvoiceMutate,
+ setSubmitPayload,
+ };
+
+ return (
+
+
+
+ );
+}
+
+const useInvoiceFormContext = () => React.useContext(InvoiceFormContext);
+
+export { InvoiceFormProvider, useInvoiceFormContext };
diff --git a/client/src/containers/Sales/Invoice/InvoiceViewTabs.js b/client/src/containers/Sales/Invoice/InvoiceViewTabs.js
index 6e9ffd1c0..95bb36c1f 100644
--- a/client/src/containers/Sales/Invoice/InvoiceViewTabs.js
+++ b/client/src/containers/Sales/Invoice/InvoiceViewTabs.js
@@ -1,65 +1,42 @@
-import React, { useEffect, useRef } from 'react';
+import React from 'react';
import { useHistory } from 'react-router';
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
-import { useParams, withRouter } from 'react-router-dom';
-import { connect } from 'react-redux';
-import { pick, debounce } from 'lodash';
+import { useParams } from 'react-router-dom';
+import { pick } from 'lodash';
import { DashboardViewsTabs } from 'components';
-import { useUpdateEffect } from 'hooks';
-import withInvoices from './withInvoices';
import withInvoiceActions from './withInvoiceActions';
-import withDashboardActions from 'containers/Dashboard/withDashboardActions';
-import withViewDetails from 'containers/Views/withViewDetails';
import { compose } from 'utils';
+import { useInvoicesListContext } from './InvoicesListProvider';
/**
* Invoices views tabs.
*/
function InvoiceViewTabs({
- //#withInvoices
- invoicesViews,
-
- // #withViewDetails
- viewItem,
-
- //#withInvoiceActions
- changeInvoiceView,
+ // #withInvoiceActions
addInvoiceTableQueries,
-
- // #withDashboardActions
- setTopbarEditView,
- changePageSubtitle,
-
- // #ownProps
- customViewChanged,
- onViewChanged,
}) {
const history = useHistory();
const { custom_view_id: customViewId = null } = useParams();
- useEffect(() => {
- setTopbarEditView(customViewId);
- changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
- }, [customViewId]);
+ // Invoices list context.
+ const { invoicesViews } = useInvoicesListContext();
const tabs = invoicesViews.map((view) => ({
...pick(view, ['name', 'id']),
}));
- const handleTabsChange = (viewId) => {
- changeInvoiceView(viewId || -1);
+ // Handle tab change.
+ const handleTabsChange = (viewId) => {
addInvoiceTableQueries({
custom_view_id: customViewId || null,
});
- setTopbarEditView(viewId);
};
// Handle click a new view tab.
const handleClickNewView = () => {
- setTopbarEditView(null);
history.push('/custom_views/invoices/new');
};
@@ -78,19 +55,6 @@ function InvoiceViewTabs({
);
}
-const mapStateToProps = (state, ownProps) => ({
- viewId: ownProps.match.params.custom_view_id,
-});
-
-const withInvoicesViewTabs = connect(mapStateToProps);
-
export default compose(
- withRouter,
- withInvoicesViewTabs,
withInvoiceActions,
- withDashboardActions,
- withViewDetails(),
- withInvoices(({ invoicesViews }) => ({
- invoicesViews,
- })),
)(InvoiceViewTabs);
diff --git a/client/src/containers/Sales/Invoice/InvoicesList.js b/client/src/containers/Sales/Invoice/InvoicesList.js
index d20dee75f..a08cf5f6a 100644
--- a/client/src/containers/Sales/Invoice/InvoicesList.js
+++ b/client/src/containers/Sales/Invoice/InvoicesList.js
@@ -1,139 +1,51 @@
-import React, { useEffect, useCallback, useMemo, useState } from 'react';
-import { Route, Switch, useHistory } from 'react-router-dom';
-import { useQuery} from 'react-query';
+import React, { useEffect } from 'react';
+import { useIntl } from 'react-intl';
import 'style/pages/SaleInvoice/List.scss';
-import { FormattedMessage as T, useIntl } from 'react-intl';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
-import DashboardInsider from 'components/Dashboard/DashboardInsider';
-
-import InvoicesDataTable from './InvoicesDataTable';
import InvoiceActionsBar from './InvoiceActionsBar';
-import InvoiceViewTabs from './InvoiceViewTabs';
+import { InvoicesListProvider } from './InvoicesListProvider';
+
+import InvoicesViewPage from './InvoicesViewPage';
import InvoicesAlerts from './InvoicesAlerts';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
-import withResourceActions from 'containers/Resources/withResourcesActions';
import withInvoices from './withInvoices';
-import withInvoiceActions from 'containers/Sales/Invoice/withInvoiceActions';
-import withViewsActions from 'containers/Views/withViewsActions';
import withAlertsActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
/**
- * Invoices list.
+ * Sale invoices list.
*/
function InvoicesList({
// #withDashboardActions
changePageTitle,
- // #withViewsActions
- requestFetchResourceViews,
- requestFetchResourceFields,
-
- //#withInvoice
+ // #withInvoice
invoicesTableQuery,
- invoicesViews,
-
- // #withAlertsActions.
- openAlert,
-
- //#withInvoiceActions
- requestFetchInvoiceTable,
-
- addInvoiceTableQueries,
}) {
- const history = useHistory();
const { formatMessage } = useIntl();
- const [selectedRows, setSelectedRows] = useState([]);
useEffect(() => {
changePageTitle(formatMessage({ id: 'invoices_list' }));
}, [changePageTitle, formatMessage]);
- const fetchResourceViews = useQuery(
- ['resource-views', 'sale_invoice'],
- (key, resourceName) => requestFetchResourceViews(resourceName),
- );
-
- const fetchResourceFields = useQuery(
- ['resource-fields', 'sale_invoice'],
- (key, resourceName) => requestFetchResourceFields(resourceName),
- );
-
- const fetchInvoices = useQuery(
- ['invoices-table', invoicesTableQuery],
- (key, query) => requestFetchInvoiceTable({ ...query }),
- );
- //handle delete Invoice
- const handleDeleteInvoice = useCallback(
- ({ id }) => {
- openAlert('invoice-delete', { invoiceId: id });
- },
- [openAlert],
- );
-
- // Handle cancel/confirm invoice deliver.
- const handleDeliverInvoice = useCallback(
- ({id}) => {
- openAlert('invoice-deliver', { invoiceId: id });
- },
- [openAlert],
- );
-
-
- const handleEditInvoice = useCallback((invoice) => {
- history.push(`/invoices/${invoice.id}/edit`);
- });
-
- // Handle filter change to re-fetch data-table.
- const handleFilterChanged = useCallback(() => {}, []);
-
- // Handle selected rows change.
- const handleSelectedRowsChange = useCallback(
- (_invoices) => {
- setSelectedRows(_invoices);
- },
- [setSelectedRows],
- );
return (
-
-
-
-
-
-
-
-
-
+
+
+
+
-
+
);
}
export default compose(
- withResourceActions,
- withInvoiceActions,
withDashboardActions,
- withViewsActions,
withInvoices(({ invoicesTableQuery }) => ({
invoicesTableQuery,
})),
diff --git a/client/src/containers/Sales/Invoice/InvoicesListProvider.js b/client/src/containers/Sales/Invoice/InvoicesListProvider.js
new file mode 100644
index 000000000..9e9a59a9e
--- /dev/null
+++ b/client/src/containers/Sales/Invoice/InvoicesListProvider.js
@@ -0,0 +1,52 @@
+import React, { createContext } from 'react';
+import DashboardInsider from 'components/Dashboard/DashboardInsider';
+import { useResourceViews, useResourceFields, useInvoices } from 'hooks/query';
+
+const InvoicesListContext = createContext();
+
+/**
+ * Accounts chart data provider.
+ */
+function InvoicesListProvider({ accountsTableQuery, ...props }) {
+ // Fetch accounts resource views and fields.
+ const { data: invoicesViews, isFetching: isViewsLoading } = useResourceViews(
+ 'sale_invoices',
+ );
+
+ // Fetch the accounts resource fields.
+ const {
+ data: invoicesFields,
+ isFetching: isFieldsLoading,
+ } = useResourceFields('sale_invoices');
+
+ // Fetch accounts list according to the given custom view id.
+ const {
+ data: { invoices, pagination },
+ isFetching: isInvoicesLoading,
+ } = useInvoices(accountsTableQuery);
+
+ // Provider payload.
+ const provider = {
+ invoices,
+ pagination,
+ invoicesFields,
+ invoicesViews,
+
+ isInvoicesLoading,
+ isFieldsLoading,
+ isViewsLoading,
+ };
+
+ return (
+
+
+
+ );
+}
+
+const useInvoicesListContext = () => React.useContext(InvoicesListContext);
+
+export { InvoicesListProvider, useInvoicesListContext };
diff --git a/client/src/containers/Sales/Invoice/InvoicesViewPage.js b/client/src/containers/Sales/Invoice/InvoicesViewPage.js
new file mode 100644
index 000000000..c6ecec85c
--- /dev/null
+++ b/client/src/containers/Sales/Invoice/InvoicesViewPage.js
@@ -0,0 +1,73 @@
+import React, { useCallback } from 'react';
+import { Switch, Route, useHistory } from 'react-router-dom';
+
+import InvoicesDataTable from './InvoicesDataTable';
+import InvoiceViewTabs from './InvoiceViewTabs';
+
+import withAlertsActions from 'containers/Alert/withAlertActions';
+import withDialogActions from 'containers/Dialog/withDialogActions';
+
+import { compose } from 'utils';
+
+/**
+ * Invoices list view page.
+ */
+function InvoicesViewPage({
+ // #withAlertActions
+ openAlert,
+}) {
+ const history = useHistory();
+
+ // Handle delete sale invoice.
+ const handleDeleteInvoice = useCallback(
+ ({ id }) => {
+ openAlert('invoice-delete', { invoiceId: id });
+ },
+ [openAlert],
+ );
+
+ // Handle cancel/confirm invoice deliver.
+ const handleDeliverInvoice = useCallback(
+ ({ id }) => {
+ openAlert('invoice-deliver', { invoiceId: id });
+ },
+ [openAlert],
+ );
+
+ // Handle edit sale invoice.
+ const handleEditInvoice = useCallback(
+ (invoice) => {
+ history.push(`/invoices/${invoice.id}/edit`);
+ },
+ [history],
+ );
+
+ // Handle selected rows change.
+ const handleSelectedRowsChange = useCallback(
+ (invoices) => {
+
+ },
+ [],
+ );
+ return (
+
+
+
+ {/* */}
+
+
+ );
+}
+
+export default compose(
+ withAlertsActions,
+ withDialogActions,
+)(InvoicesViewPage)
\ No newline at end of file
diff --git a/client/src/containers/Sales/PaymentReceive/PaymentReceiptsListProvider.js b/client/src/containers/Sales/PaymentReceive/PaymentReceiptsListProvider.js
new file mode 100644
index 000000000..4848eef7a
--- /dev/null
+++ b/client/src/containers/Sales/PaymentReceive/PaymentReceiptsListProvider.js
@@ -0,0 +1,52 @@
+import React, { createContext } from 'react';
+import DashboardInsider from 'components/Dashboard/DashboardInsider';
+import { useResourceViews, useResourceFields, usePaymentReceives } from 'hooks/query';
+
+const PaymentReceivesListContext = createContext();
+
+/**
+ * Payment receives list data provider.
+ */
+function PaymentReceivesListProvider({ query, ...props }) {
+ // Fetch payment receives resource views and fields.
+ const { data: paymentReceivesViews, isFetching: isViewsLoading } = useResourceViews(
+ 'payment_receives',
+ );
+
+ // Fetch the payment receives resource fields.
+ const {
+ data: paymentReceivesFields,
+ isFetching: isFieldsLoading,
+ } = useResourceFields('payment_receives');
+
+ // Fetch payment receives list according to the given custom view id.
+ const {
+ data: { paymentReceives, pagination },
+ isFetching: isPaymentReceivesLoading,
+ } = usePaymentReceives(query);
+
+ // Provider payload.
+ const provider = {
+ paymentReceives,
+ pagination,
+ paymentReceivesFields,
+ paymentReceivesViews,
+
+ isPaymentReceivesLoading,
+ isFieldsLoading,
+ isViewsLoading,
+ };
+
+ return (
+
+
+
+ );
+}
+
+const usePaymentReceivesListContext = () => React.useContext(PaymentReceivesListContext);
+
+export { PaymentReceivesListProvider, usePaymentReceivesListContext };
diff --git a/client/src/containers/Sales/PaymentReceive/PaymentReceiveFloatingActions.js b/client/src/containers/Sales/PaymentReceive/PaymentReceiveFloatingActions.js
index 17735b603..03a14dc57 100644
--- a/client/src/containers/Sales/PaymentReceive/PaymentReceiveFloatingActions.js
+++ b/client/src/containers/Sales/PaymentReceive/PaymentReceiveFloatingActions.js
@@ -12,7 +12,7 @@ import {
import { FormattedMessage as T } from 'react-intl';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
-import { useFormikContext } from 'formik';
+
import { saveInvoke } from 'utils';
import { Icon } from 'components';
diff --git a/client/src/containers/Sales/PaymentReceive/PaymentReceiveViewTabs.js b/client/src/containers/Sales/PaymentReceive/PaymentReceiveViewTabs.js
index 00a476b98..c5cb2fcd1 100644
--- a/client/src/containers/Sales/PaymentReceive/PaymentReceiveViewTabs.js
+++ b/client/src/containers/Sales/PaymentReceive/PaymentReceiveViewTabs.js
@@ -1,83 +1,44 @@
-import React, { useEffect, useRef } from 'react';
+import React from 'react';
import { useHistory } from 'react-router';
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
-import { useParams, withRouter } from 'react-router-dom';
+import { useParams } from 'react-router-dom';
import { FormattedMessage as T } from 'react-intl';
-import { connect } from 'react-redux';
-import { pick, debounce } from 'lodash';
+import { pick } from 'lodash';
import { DashboardViewsTabs } from 'components';
-import { useUpdateEffect } from 'hooks';
-import withPaymentReceives from './withPaymentReceives';
import withPaymentReceivesActions from './withPaymentReceivesActions';
-import withDashboardActions from 'containers/Dashboard/withDashboardActions';
-import withViewDetails from 'containers/Views/withViewDetails';
+import { usePaymentReceivesListContext } from './PaymentReceiptsListProvider';
import { compose } from 'utils';
+/**
+ * Payment receive view tabs.
+ */
function PaymentReceiveViewTabs({
- //#withPaymentReceives
- paymentReceivesViews,
-
//#withPaymentReceivesActions
- changePaymentReceiveView,
addPaymentReceivesTableQueries,
-
- // #withViewDetails
- viewItem,
-
- // #withDashboardActions
- setTopbarEditView,
- changePageSubtitle,
-
- //#Own Props
- customViewChanged,
- onViewChanged,
}) {
const history = useHistory();
+ const { paymentReceivesViews } = usePaymentReceivesListContext();
+
const { custom_view_id: customViewId = null } = useParams();
-
- useEffect(() => {
- changePaymentReceiveView(customViewId || -1);
- setTopbarEditView(customViewId);
- changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
-
- addPaymentReceivesTableQueries({
- custom_view_id: customViewId,
- });
- return () => {
- setTopbarEditView(null);
- changePageSubtitle('');
- changePaymentReceiveView(null);
- };
- }, [customViewId, addPaymentReceivesTableQueries, changePaymentReceiveView]);
-
- useUpdateEffect(() => {
- onViewChanged && onViewChanged(customViewId);
- }, [customViewId]);
-
- const debounceChangeHistory = useRef(
- debounce((toUrl) => {
- history.push(toUrl);
- }, 250),
- );
-
- const handleTabsChange = (viewId) => {
- const toPath = viewId ? `${viewId}/custom_view` : '';
- debounceChangeHistory.current(`/payment-receives/${toPath}`);
- setTopbarEditView(viewId);
- };
+
const tabs = paymentReceivesViews.map((view) => ({
...pick(view, ['name', 'id']),
}));
// Handle click a new view tab.
const handleClickNewView = () => {
- setTopbarEditView(null);
history.push('/custom_views/payment-receives/new');
};
+ const handleTabsChange = (viewId) => {
+ addPaymentReceivesTableQueries({
+ custom_view_id: viewId || null,
+ });
+ }
+
return (
@@ -93,19 +54,6 @@ function PaymentReceiveViewTabs({
);
}
-const mapStateToProps = (state, ownProps) => ({
- viewId: ownProps.match.params.custom_view_id,
-});
-
-const withPaymentReceivesViewTabs = connect(mapStateToProps);
-
export default compose(
- withRouter,
- withPaymentReceivesViewTabs,
withPaymentReceivesActions,
- withDashboardActions,
- withViewDetails(),
- withPaymentReceives(({ paymentReceivesViews }) => ({
- paymentReceivesViews,
- })),
)(PaymentReceiveViewTabs);
diff --git a/client/src/containers/Sales/PaymentReceive/PaymentReceivesList.js b/client/src/containers/Sales/PaymentReceive/PaymentReceivesList.js
index 21d5093b0..cb24b5bef 100644
--- a/client/src/containers/Sales/PaymentReceive/PaymentReceivesList.js
+++ b/client/src/containers/Sales/PaymentReceive/PaymentReceivesList.js
@@ -1,107 +1,48 @@
-import React, { useEffect, useCallback, useState } from 'react';
-import { Route, Switch, useHistory } from 'react-router-dom';
-import { useQuery } from 'react-query';
+import React, { useEffect } from 'react';
+import { useIntl } from 'react-intl';
-import { FormattedMessage as T, useIntl } from 'react-intl';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
-import DashboardInsider from 'components/Dashboard/DashboardInsider';
-
-import PaymentReceivesDataTable from './PaymentReceivesDataTable';
import PaymentReceiveActionsBar from './PaymentReceiveActionsBar';
-import PaymentReceiveViewTabs from './PaymentReceiveViewTabs';
import PaymentReceiveAlerts from './PaymentReceiveAlerts';
+import { PaymentReceivesListProvider } from './PaymentReceiptsListProvider';
+import PaymentReceivesViewPage from './PaymentReceivesViewPage';
+
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
-import withResourceActions from 'containers/Resources/withResourcesActions';
import withPaymentReceives from './withPaymentReceives';
-import withPaymentReceivesActions from './withPaymentReceivesActions';
-import withAlertsActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
+/**
+ * Payment receives list.
+ */
function PaymentReceiveList({
// #withDashboardActions
changePageTitle,
- //#withPaymentReceives
+ // #withPaymentReceives
paymentReceivesTableQuery,
-
- // #withAlertsActions.
- openAlert,
-
- //#withPaymentReceivesActions
- requestFetchPaymentReceiveTable,
}) {
- const history = useHistory();
const { formatMessage } = useIntl();
- const [selectedRows, setSelectedRows] = useState([]);
-
+
useEffect(() => {
changePageTitle(formatMessage({ id: 'payment_Receives_list' }));
}, [changePageTitle, formatMessage]);
- const fetchPaymentReceives = useQuery(
- ['paymentReceives-table', paymentReceivesTableQuery],
- () => requestFetchPaymentReceiveTable(),
- );
-
- // Handle delete Payment Receive
- const handleDeletePaymentReceive = useCallback(
- ({ id }) => {
- openAlert('payment-receive-delete', { paymentReceiveId: id });
- },
- [openAlert],
- );
-
- const handleEditPaymentReceive = useCallback((payment) => {
- history.push(`/payment-receives/${payment.id}/edit`);
- });
-
- // Handle filter change to re-fetch data-table.
- const handleFilterChanged = useCallback(() => {}, [fetchPaymentReceives]);
-
- // Handle selected rows change.
- const handleSelectedRowsChange = useCallback(
- (_payment) => {
- setSelectedRows(_payment);
- },
- [setSelectedRows],
- );
-
return (
-
-
+
+
+
-
-
-
-
-
-
+
-
+
);
}
export default compose(
- withResourceActions,
- withPaymentReceivesActions,
withDashboardActions,
withPaymentReceives(({ paymentReceivesTableQuery }) => ({
paymentReceivesTableQuery,
})),
- withAlertsActions,
)(PaymentReceiveList);
diff --git a/client/src/containers/Sales/PaymentReceive/PaymentReceivesListProvider.js b/client/src/containers/Sales/PaymentReceive/PaymentReceivesListProvider.js
new file mode 100644
index 000000000..e8ab476ec
--- /dev/null
+++ b/client/src/containers/Sales/PaymentReceive/PaymentReceivesListProvider.js
@@ -0,0 +1,58 @@
+import React, { createContext } from 'react';
+import DashboardInsider from 'components/Dashboard/DashboardInsider';
+import {
+ useResourceViews,
+ useResourceFields,
+ usePaymentReceives,
+} from 'hooks/query';
+
+const PaymentReceivesListContext = createContext();
+
+/**
+ * Payment receives data provider.
+ */
+function PaymentReceivesListProvider({ query, ...props }) {
+ // Fetch accounts resource views and fields.
+ const {
+ data: paymentReceivesViews,
+ isFetching: isViewsLoading,
+ } = useResourceViews('payment_receives');
+
+ // Fetch the accounts resource fields.
+ const {
+ data: paymentReceivesFields,
+ isFetching: isFieldsLoading,
+ } = useResourceFields('payment_receives');
+
+ // Fetch accounts list according to the given custom view id.
+ const {
+ data: { paymentReceives, pagination },
+ isFetching: isPaymentReceivesLoading,
+ } = usePaymentReceives(query);
+
+ // Provider payload.
+ const provider = {
+ paymentReceives,
+ paymentReceivesViews,
+ paymentReceivesFields,
+ pagination,
+
+ isViewsLoading,
+ isFieldsLoading,
+ isPaymentReceivesLoading,
+ };
+
+ return (
+
+
+
+ );
+}
+
+const usePaymentReceivesListContext = () =>
+ React.useContext(PaymentReceivesListContext);
+
+export { PaymentReceivesListProvider, usePaymentReceivesListContext };
diff --git a/client/src/containers/Sales/PaymentReceive/PaymentReceivesViewPage.js b/client/src/containers/Sales/PaymentReceive/PaymentReceivesViewPage.js
new file mode 100644
index 000000000..791c1d37e
--- /dev/null
+++ b/client/src/containers/Sales/PaymentReceive/PaymentReceivesViewPage.js
@@ -0,0 +1,55 @@
+import React, { useCallback } from 'react';
+import { Switch, Route, useHistory } from 'react-router-dom';
+
+import PaymentReceivesDataTable from './PaymentReceivesDataTable';
+import PaymentReceiveViewTabs from './PaymentReceiveViewTabs';
+
+import withAlertsActions from 'containers/Alert/withAlertActions';
+import withDialogActions from 'containers/Dialog/withDialogActions';
+
+import { compose } from 'utils';
+
+/**
+ * Payment receives view page.
+ */
+function PaymentReceivesViewPage({
+ // #withAlertActions
+ openAlert,
+}) {
+ const history = useHistory();
+
+ // Handle delete Payment Receive
+ const handleDeletePaymentReceive = ({ id }) => {
+ openAlert('payment-receive-delete', { paymentReceiveId: id });
+ };
+
+ // Handle edit payment receive.
+ const handleEditPaymentReceive = (payment) => {
+ history.push(`/payment-receives/${payment.id}/edit`);
+ };
+
+ return (
+
+
+
+
+ {/* */}
+
+
+ );
+}
+
+export default compose(
+ withAlertsActions,
+ withDialogActions,
+)(PaymentReceivesViewPage)
\ No newline at end of file
diff --git a/client/src/containers/Sales/Receipt/ReceiptActionsBar.js b/client/src/containers/Sales/Receipt/ReceiptActionsBar.js
index 26cff7239..ff411b79b 100644
--- a/client/src/containers/Sales/Receipt/ReceiptActionsBar.js
+++ b/client/src/containers/Sales/Receipt/ReceiptActionsBar.js
@@ -1,10 +1,8 @@
-import React, { useCallback, useState, useMemo } from 'react';
+import React, { useState, useMemo } from 'react';
import Icon from 'components/Icon';
import {
Button,
Classes,
- Menu,
- MenuItem,
Popover,
NavbarDivider,
NavbarGroup,
@@ -14,75 +12,50 @@ import {
} from '@blueprintjs/core';
import classNames from 'classnames';
-import { useRouteMatch, useHistory } from 'react-router-dom';
+import { useHistory } from 'react-router-dom';
import { FormattedMessage as T, useIntl } from 'react-intl';
-import { connect } from 'react-redux';
import { If, DashboardActionViewsList } from 'components';
import FilterDropdown from 'components/FilterDropdown';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
-import withResourceDetail from 'containers/Resources/withResourceDetails';
-import withDialogActions from 'containers/Dialog/withDialogActions';
+
import withReceiptActions from './withReceiptActions';
-import withReceipts from './withReceipts';
+import { useReceiptsListContext } from './ReceiptsListProvider';
import { compose } from 'utils';
+/**
+ * Receipts actions bar.
+ */
function ReceiptActionsBar({
- // #withResourceDetail
- resourceFields,
-
- //#withReceipts
- receiptview,
//#withReceiptActions
addReceiptsTableQueries,
- changeReceiptView,
-
- //#OWn Props
- onFilterChanged,
- selectedRows = [],
}) {
const history = useHistory();
- const [filterCount, setFilterCount] = useState(0);
const { formatMessage } = useIntl();
+
+ const [filterCount, setFilterCount] = useState(0);
- const onClickNewReceipt = useCallback(() => {
+ // Sale receipts list context.
+ const { receiptsViews } = useReceiptsListContext();
+
+ const onClickNewReceipt = () => {
history.push('/receipts/new');
- }, [history]);
-
- const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
- selectedRows,
- ]);
-
+ };
+
const handleTabChange = (viewId) => {
- changeReceiptView(viewId.id || -1);
addReceiptsTableQueries({
custom_view_id: viewId.id || null,
});
};
-
- // const filterDropdown = FilterDropdown({
- // initialCondition: {
- // fieldKey: '',
- // compatator: '',
- // value: '',
- // },
- // fields: resourceFields,
- // onFilterChange: (filterConditions) => {
- // addReceiptsTableQueries({
- // filter_roles: filterConditions || '',
- // });
- // onFilterChanged && onFilterChange(filterConditions);
- // },
- // });
-
+
return (
@@ -111,7 +84,7 @@ function ReceiptActionsBar({
icon={}
/>
-
+
}
@@ -139,19 +112,7 @@ function ReceiptActionsBar({
);
}
-const mapStateToProps = (state, props) => ({
- resourceName: 'sales_receipts',
-});
-
-const withReceiptActionsBar = connect(mapStateToProps);
export default compose(
- withReceiptActionsBar,
- withResourceDetail(({ resourceFields }) => ({
- resourceFields,
- })),
- withReceipts(({ receiptview }) => ({
- receiptview,
- })),
withReceiptActions,
)(ReceiptActionsBar);
diff --git a/client/src/containers/Sales/Receipt/ReceiptForm.js b/client/src/containers/Sales/Receipt/ReceiptForm.js
index 2527b0c59..6a644e787 100644
--- a/client/src/containers/Sales/Receipt/ReceiptForm.js
+++ b/client/src/containers/Sales/Receipt/ReceiptForm.js
@@ -1,31 +1,30 @@
-import React, { useMemo, useCallback, useEffect, useState } from 'react';
+import React, { useMemo, useCallback, useEffect } from 'react';
import { Formik, Form } from 'formik';
import moment from 'moment';
import { Intent } from '@blueprintjs/core';
-import { FormattedMessage as T, useIntl } from 'react-intl';
-import { pick, sumBy } from 'lodash';
+import { useIntl } from 'react-intl';
+import { pick, sumBy, isEmpty } from 'lodash';
import classNames from 'classnames';
import { useHistory } from 'react-router-dom';
import { CLASSES } from 'common/classes';
import { ERROR } from 'common/errors';
-
import {
EditReceiptFormSchema,
CreateReceiptFormSchema,
} from './ReceiptForm.schema';
+import 'style/pages/SaleReceipt/PageForm.scss';
+
+import { useReceiptFormContext } from './ReceiptFormProvider';
+
import ReceiptFromHeader from './ReceiptFormHeader';
import ReceiptFormBody from './ReceiptFormBody';
import ReceiptFormFloatingActions from './ReceiptFormFloatingActions';
import ReceiptFormFooter from './ReceiptFormFooter';
import ReceiptNumberWatcher from './ReceiptNumberWatcher';
-import withReceiptActions from './withReceiptActions';
-import withReceiptDetail from './withReceiptDetail';
-
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
-import withMediaActions from 'containers/Media/withMediaActions';
import withSettings from 'containers/Settings/withSettings';
import { AppToaster } from 'components';
@@ -34,10 +33,9 @@ import {
repeatValue,
orderingLinesIndexes,
defaultToTransform,
+ transactionNumber,
} from 'utils';
-import 'style/pages/SaleReceipt/PageForm.scss'
-
const MIN_LINES_NUMBER = 4;
const defaultReceipt = {
@@ -65,17 +63,6 @@ const defaultInitialValues = {
* Receipt form.
*/
function ReceiptForm({
- // #withMedia
- requestSubmitMedia,
- requestDeleteMedia,
-
- // #withReceiptActions
- requestSubmitReceipt,
- requestEditReceipt,
-
- // #withReceiptDetail
- receipt,
-
// #withDashboard
changePageTitle,
changePageSubtitle,
@@ -84,22 +71,26 @@ function ReceiptForm({
receiptNextNumber,
receiptNumberPrefix,
preferredDepositAccount,
-
- //#own Props
- receiptId,
- onFormSubmit,
- onCancelForm,
}) {
const { formatMessage } = useIntl();
const history = useHistory();
- const [submitPayload, setSubmitPayload] = useState({});
+ // Receipt form context.
+ const {
+ receiptId,
+ receipt,
+ editReceiptMutate,
+ createReceiptMutate,
+ submitPayload
+ } = useReceiptFormContext();
+
const isNewMode = !receiptId;
- const receiptNumber = receiptNumberPrefix
- ? `${receiptNumberPrefix}-${receiptNextNumber}`
- : receiptNextNumber;
-
+ // The next receipt number.
+ const receiptNumber = transactionNumber(
+ receiptNumberPrefix,
+ receiptNextNumber,
+ );
useEffect(() => {
const transactionNumber = !isNewMode
? receipt.receipt_number
@@ -125,7 +116,7 @@ function ReceiptForm({
// Initial values in create and edit mode.
const initialValues = useMemo(
() => ({
- ...(receipt
+ ...(!isEmpty(receipt)
? {
...pick(receipt, Object.keys(defaultInitialValues)),
entries: [
@@ -145,7 +136,7 @@ function ReceiptForm({
entries: orderingLinesIndexes(defaultInitialValues.entries),
}),
}),
- [receipt],
+ [receipt, preferredDepositAccount, receiptNumber],
);
// Transform response error to fields.
@@ -167,7 +158,6 @@ function ReceiptForm({
const entries = values.entries.filter(
(item) => item.item_id && item.quantity,
);
-
const totalQuantity = sumBy(entries, (entry) => parseInt(entry.quantity));
if (totalQuantity === 0) {
@@ -218,9 +208,9 @@ function ReceiptForm({
};
if (receipt && receipt.id) {
- requestEditReceipt(receipt.id, form).then(onSuccess).catch(onError);
+ editReceiptMutate(receipt.id, form).then(onSuccess).catch(onError);
} else {
- requestSubmitReceipt(form).then(onSuccess).catch(onError);
+ createReceiptMutate(form).then(onSuccess).catch(onError);
}
};
@@ -233,20 +223,6 @@ function ReceiptForm({
[changePageSubtitle],
);
- const handleSubmitClick = useCallback(
- (event, payload) => {
- setSubmitPayload({ ...payload });
- },
- [setSubmitPayload],
- );
-
- const handleCancelClick = useCallback(
- (event) => {
- history.goBack();
- },
- [history],
- );
-
return (
- {({ isSubmitting}) => (
-
- )}
+
);
}
export default compose(
- withReceiptActions,
- withReceiptDetail(),
withDashboardActions,
- withMediaActions,
withSettings(({ receiptSettings }) => ({
receiptNextNumber: receiptSettings?.nextNumber,
receiptNumberPrefix: receiptSettings?.numberPrefix,
diff --git a/client/src/containers/Sales/Receipt/ReceiptFormBody.js b/client/src/containers/Sales/Receipt/ReceiptFormBody.js
index 61d8779e2..fc8a467ca 100644
--- a/client/src/containers/Sales/Receipt/ReceiptFormBody.js
+++ b/client/src/containers/Sales/Receipt/ReceiptFormBody.js
@@ -3,11 +3,15 @@ import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import EditableItemsEntriesTable from 'containers/Entries/EditableItemsEntriesTable';
+import { useReceiptFormContext } from './ReceiptFormProvider';
export default function ExpenseFormBody({ defaultReceipt }) {
+ const { items } = useReceiptFormContext();
+
return (
diff --git a/client/src/containers/Sales/Receipt/ReceiptFormFloatingActions.js b/client/src/containers/Sales/Receipt/ReceiptFormFloatingActions.js
index 2754691a8..b2a36f445 100644
--- a/client/src/containers/Sales/Receipt/ReceiptFormFloatingActions.js
+++ b/client/src/containers/Sales/Receipt/ReceiptFormFloatingActions.js
@@ -1,4 +1,4 @@
-import React, { useCallback } from 'react';
+import React from 'react';
import {
Intent,
Button,
@@ -13,70 +13,57 @@ import { FormattedMessage as T } from 'react-intl';
import { useFormikContext } from 'formik';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
-import { saveInvoke } from 'utils';
import { If, Icon } from 'components';
+import { useReceiptFormContext } from './ReceiptFormProvider';
/**
* Receipt floating actions bar.
*/
-export default function ReceiptFormFloatingActions({
- isSubmitting,
- receipt,
- onSubmitClick,
- onCancelClick,
-}) {
- const { resetForm, submitForm } = useFormikContext();
+export default function ReceiptFormFloatingActions() {
+ // Formik context.
+ const { resetForm, submitForm, isSubmitting } = useFormikContext();
+ // Receipt form context.
+ const { receipt, setSubmitPayload } = useReceiptFormContext();
+
+ // Handle submit & close button click.
const handleSubmitCloseBtnClick = (event) => {
- saveInvoke(onSubmitClick, event, {
- redirect: true,
- status: true,
- });
+ setSubmitPayload({ redirect: true, status: true });
+ submitForm();
};
+ // Handle submit, close & new button click.
const handleSubmitCloseAndNewBtnClick = (event) => {
+ setSubmitPayload({ redirect: false, status: true, resetForm: true });
submitForm();
- saveInvoke(onSubmitClick, event, {
- redirect: false,
- status: true,
- resetForm: true,
- });
};
+ // Handle submit, close & continue editing button click.
const handleSubmitCloseContinueEditingBtnClick = (event) => {
+ setSubmitPayload({ redirect: false, status: true });
submitForm();
- saveInvoke(onSubmitClick, event, {
- redirect: false,
- status: true,
- });
};
+ // Handle submit & draft button click.
const handleSubmitDraftBtnClick = (event) => {
- saveInvoke(onSubmitClick, event, {
- redirect: true,
- status: false,
- });
+ setSubmitPayload({ redirect: true, status: false });
+ submitForm();
};
+ // Handle submit, draft & new button click.
const handleSubmitDraftAndNewBtnClick = (event) => {
+ setSubmitPayload({ redirect: false, status: false, resetForm: true });
submitForm();
- saveInvoke(onSubmitClick, event, {
- redirect: false,
- status: false,
- resetForm: true,
- });
};
const handleSubmitDraftContinueEditingBtnClick = (event) => {
+ setSubmitPayload({ redirect: false, status: false });
submitForm();
- saveInvoke(onSubmitClick, event, {
- redirect: false,
- status: false,
- });
};
+ // Handle cancel button click.
const handleCancelBtnClick = (event) => {
- saveInvoke(onCancelClick, event);
+
};
const handleClearBtnClick = (event) => {
@@ -91,7 +78,6 @@ export default function ReceiptFormFloatingActions({
}
/>
@@ -124,7 +110,6 @@ export default function ReceiptFormFloatingActions({
}
/>
@@ -158,7 +143,6 @@ export default function ReceiptFormFloatingActions({
}
/>
diff --git a/client/src/containers/Sales/Receipt/ReceiptFormHeaderFields.js b/client/src/containers/Sales/Receipt/ReceiptFormHeaderFields.js
index a6f3b2861..8c9e6b291 100644
--- a/client/src/containers/Sales/Receipt/ReceiptFormHeaderFields.js
+++ b/client/src/containers/Sales/Receipt/ReceiptFormHeaderFields.js
@@ -20,10 +20,7 @@ import {
InputPrependButton,
} from 'components';
-import withCustomers from 'containers/Customers/withCustomers';
-import withAccounts from 'containers/Accounts/withAccounts';
import withDialogActions from 'containers/Dialog/withDialogActions';
-
import {
momentFormatter,
compose,
@@ -32,20 +29,20 @@ import {
handleDateChange,
inputIntent,
} from 'utils';
+import { useReceiptFormContext } from './ReceiptFormProvider';
+/**
+ * Receipt form header fields.
+ */
function ReceiptFormHeader({
- //#withCustomers
- customers,
-
- //#withAccouts
- accountsList,
-
//#withDialogActions
openDialog,
// #ownProps
onReceiptNumberChanged,
}) {
+ const { accounts, customers } = useReceiptFormContext();
+
const handleReceiptNumberChange = useCallback(() => {
openDialog('receipt-number-form', {});
}, [openDialog]);
@@ -92,13 +89,13 @@ function ReceiptFormHeader({
helperText={
}
>
{
form.setFieldValue('deposit_account_id', account.id);
}}
defaultSelectText={}
selectedAccountId={value}
- filterByTypes={['current_asset']}
+ // filterByTypes={['current_asset']}
popoverFill={true}
/>
@@ -185,11 +182,5 @@ function ReceiptFormHeader({
}
export default compose(
- withCustomers(({ customers }) => ({
- customers,
- })),
- withAccounts(({ accountsList }) => ({
- accountsList,
- })),
withDialogActions,
)(ReceiptFormHeader);
diff --git a/client/src/containers/Sales/Receipt/ReceiptFormPage.js b/client/src/containers/Sales/Receipt/ReceiptFormPage.js
index dc94d2ea9..1d96c5f04 100644
--- a/client/src/containers/Sales/Receipt/ReceiptFormPage.js
+++ b/client/src/containers/Sales/Receipt/ReceiptFormPage.js
@@ -1,41 +1,22 @@
-import React, { useCallback, useEffect } from 'react';
+import React, { useEffect } from 'react';
import { useParams, useHistory } from 'react-router-dom';
-import { useQuery } from 'react-query';
import ReceiptFrom from './ReceiptForm';
-import DashboardInsider from 'components/Dashboard/DashboardInsider';
+import { ReceiptFormProvider } from './ReceiptFormProvider';
-import withCustomersActions from 'containers/Customers/withCustomersActions';
-import withAccountsActions from 'containers/Accounts/withAccountsActions';
-import withItemsActions from 'containers/Items/withItemsActions';
-import withReceiptActions from './withReceiptActions';
-import withSettingsActions from 'containers/Settings/withSettingsActions';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import { compose } from 'utils';
+/**
+ * Receipt form page.
+ */
function ReceiptFormPage({
- //#withwithAccountsActions
- requestFetchAccounts,
-
- //#withCustomersActions
- requestFetchCustomers,
-
- //#withItemsActions
- requestFetchItems,
-
- //#withReceiptsActions
- requestFetchReceipt,
-
- // #withSettingsActions
- requestFetchOptions,
-
// #withDashboardActions
setSidebarShrink,
resetSidebarPreviousExpand,
setDashboardBackLink,
}) {
- const history = useHistory();
const { id } = useParams();
useEffect(() => {
@@ -52,59 +33,25 @@ function ReceiptFormPage({
};
}, [resetSidebarPreviousExpand, setSidebarShrink, setDashboardBackLink]);
- const fetchReceipt = useQuery(
- ['receipt', id],
- (key, _id) => requestFetchReceipt(_id),
- { enabled: !!id },
- );
- const fetchAccounts = useQuery('accounts-list', (key) =>
- requestFetchAccounts(),
- );
+
+ // const handleFormSubmit = useCallback(
+ // (payload) => {
+ // payload.redirect && history.push('/receipts');
+ // },
+ // [history],
+ // );
- const fetchCustomers = useQuery('customers-table', () =>
- requestFetchCustomers({}),
- );
-
- // Handle fetch Items data table or list
- const fetchItems = useQuery('items-table', () => requestFetchItems({}));
-
- const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
-
- const handleFormSubmit = useCallback(
- (payload) => {
- payload.redirect && history.push('/receipts');
- },
- [history],
- );
-
- const handleCancel = useCallback(() => {
- history.goBack();
- }, [history]);
+ // const handleCancel = useCallback(() => {
+ // history.goBack();
+ // }, [history]);
return (
-
-
-
+
+
+
);
}
export default compose(
- withReceiptActions,
- withCustomersActions,
- withItemsActions,
- withAccountsActions,
- withSettingsActions,
withDashboardActions,
)(ReceiptFormPage);
diff --git a/client/src/containers/Sales/Receipt/ReceiptFormProvider.js b/client/src/containers/Sales/Receipt/ReceiptFormProvider.js
new file mode 100644
index 000000000..99baf43ad
--- /dev/null
+++ b/client/src/containers/Sales/Receipt/ReceiptFormProvider.js
@@ -0,0 +1,85 @@
+import React, { createContext, useState } from 'react';
+import DashboardInsider from 'components/Dashboard/DashboardInsider';
+import {
+ useReceipt,
+ useAccounts,
+ useSettings,
+ useCustomers,
+ useItems,
+ useCreateReceipt,
+ useEditReceipt
+} from 'hooks/query';
+
+const ReceiptFormContext = createContext();
+
+/**
+ * Receipt form provider.
+ */
+function ReceiptFormProvider({ receiptId, ...props }) {
+ // Fetch sale receipt details.
+ const { data: receipt, isFetching: isReceiptLoading } = useReceipt(
+ receiptId,
+ {
+ enabled: !!receiptId,
+ },
+ );
+ // Fetch accounts list.
+ const { data: accounts, isFetching: isAccountsLoading } = useAccounts();
+
+ // Fetch customers list.
+ const {
+ data: { customers },
+ isFetching: isCustomersLoading,
+ } = useCustomers();
+
+ // Handle fetch Items data table or list
+ const {
+ data: { items },
+ isFetching: isItemsLoading,
+ } = useItems();
+
+ // Fetch receipt settings.
+ const { isFetching: isSettingLoading } = useSettings();
+
+ const { mutateAsync: createReceiptMutate } = useCreateReceipt();
+ const { mutateAsync: editReceiptMutate } = useEditReceipt();
+
+ const [submitPayload, setSubmitPayload] = useState({});
+
+ const provider = {
+ receiptId,
+ receipt,
+ accounts,
+ customers,
+ items,
+ submitPayload,
+
+ isReceiptLoading,
+ isAccountsLoading,
+ isCustomersLoading,
+ isItemsLoading,
+ isSettingLoading,
+
+ createReceiptMutate,
+ editReceiptMutate,
+ setSubmitPayload
+ };
+ return (
+
+
+
+ );
+}
+
+const useReceiptFormContext = () => React.useContext(ReceiptFormContext);
+
+export { ReceiptFormProvider, useReceiptFormContext };
diff --git a/client/src/containers/Sales/Receipt/ReceiptViewTabs.js b/client/src/containers/Sales/Receipt/ReceiptViewTabs.js
index 87b0234bb..8be8a2871 100644
--- a/client/src/containers/Sales/Receipt/ReceiptViewTabs.js
+++ b/client/src/containers/Sales/Receipt/ReceiptViewTabs.js
@@ -1,69 +1,32 @@
-import React, { useEffect, useRef } from 'react';
+import React from 'react';
import { useHistory } from 'react-router';
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
-import { useParams, withRouter } from 'react-router-dom';
-import { connect } from 'react-redux';
-import { pick, debounce } from 'lodash';
+import { useParams } from 'react-router-dom';
+import { pick } from 'lodash';
import { DashboardViewsTabs } from 'components';
-import { useUpdateEffect } from 'hooks';
-import withReceipts from './withReceipts';
import withReceiptActions from './withReceiptActions';
-import withDashboardActions from 'containers/Dashboard/withDashboardActions';
-import withViewDetails from 'containers/Views/withViewDetails';
import { compose } from 'utils';
+import { useReceiptsListContext } from './ReceiptsListProvider';
/**
* Receipt views tabs.
*/
-function ReceiptViewTabs({
- //#withReceipts
- receiptview,
- // #withViewDetails
- viewItem,
-
- //#withReceiptActions
- changeReceiptView,
- addReceiptsTableQueries,
-
- // #withDashboardActions
- setTopbarEditView,
- changePageSubtitle,
-
- //# own Props
- customViewChanged,
- onViewChanged,
-}) {
- const history = useHistory();
+function ReceiptViewTabs({ addReceiptsTableQueries }) {
const { custom_view_id: customViewId = null } = useParams();
- useEffect(() => {
- setTopbarEditView(customViewId);
- changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
- // changeReceiptView(customViewId || -1);
- // addReceiptsTableQueries({
- // custom_view_id: customViewId || null,
- // });
- }, [customViewId, addReceiptsTableQueries]);
+ const { receiptsViews } = useReceiptsListContext();
- const tabs = receiptview.map((view) => ({
+ const tabs = receiptsViews.map((view) => ({
...pick(view, ['name', 'id']),
}));
const handleTabsChange = (viewId) => {
- changeReceiptView(viewId || -1);
addReceiptsTableQueries({
custom_view_id: viewId || null,
});
- setTopbarEditView(viewId);
- };
-
- // Handle click a new view tab.
- const handleClickNewView = () => {
- setTopbarEditView(null);
- history.push('/custom_views/receipts/new');
};
return (
@@ -80,17 +43,4 @@ function ReceiptViewTabs({
);
}
-const mapStateToProps = (state, ownProps) => ({
- viewId: ownProps.match.params.custom_view_id,
-});
-
-const withReceiptsViewTabs = connect(mapStateToProps);
-
-export default compose(
- withRouter,
- withReceiptsViewTabs,
- withReceiptActions,
- withDashboardActions,
- withViewDetails(),
- withReceipts(({ receiptview }) => ({ receiptview })),
-)(ReceiptViewTabs);
+export default compose(withReceiptActions)(ReceiptViewTabs);
diff --git a/client/src/containers/Sales/Receipt/ReceiptsList.js b/client/src/containers/Sales/Receipt/ReceiptsList.js
index 0d0aae47f..e97e374a1 100644
--- a/client/src/containers/Sales/Receipt/ReceiptsList.js
+++ b/client/src/containers/Sales/Receipt/ReceiptsList.js
@@ -1,127 +1,50 @@
-import React, { useEffect, useCallback, useState } from 'react';
-import { Route, Switch, useHistory } from 'react-router-dom';
-import { useQuery } from 'react-query';
+import React, { useEffect } from 'react';
+import { useIntl } from 'react-intl';
-import { FormattedMessage as T, useIntl } from 'react-intl';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
-import DashboardInsider from 'components/Dashboard/DashboardInsider';
-import ReceiptsDataTable from './ReceiptsDataTable';
import ReceiptActionsBar from './ReceiptActionsBar';
-import ReceiptViewTabs from './ReceiptViewTabs';
+import ReceiptsViewPage from './ReceiptsViewPage';
import ReceiptsAlerts from './ReceiptsAlerts';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
-import withResourceActions from 'containers/Resources/withResourcesActions';
import withReceipts from './withReceipts';
-import withReceiptActions from './withReceiptActions';
-import withViewsActions from 'containers/Views/withViewsActions';
-import withAlertsActions from 'containers/Alert/withAlertActions';
+
+import { ReceiptsListProvider } from './ReceiptsListProvider';
import { compose } from 'utils';
+/**
+ * Receipts list page.
+ */
function ReceiptsList({
// #withDashboardActions
changePageTitle,
- // #withViewsActions
- requestFetchResourceViews,
-
- //#withReceipts
+ // #withReceipts
receiptTableQuery,
-
- // #withAlertsActions,
- openAlert,
-
- //#withReceiptActions
- requestFetchReceiptsTable,
- addReceiptsTableQueries,
}) {
- const history = useHistory();
const { formatMessage } = useIntl();
- const [selectedRows, setSelectedRows] = useState([]);
-
- const fetchReceipts = useQuery(
- ['receipts-table', receiptTableQuery],
- (key, query) => requestFetchReceiptsTable({ ...query }),
- );
-
- const fetchResourceViews = useQuery(
- ['resource-views', 'sale_receipt'],
- (key, resourceName) => requestFetchResourceViews(resourceName),
- );
useEffect(() => {
changePageTitle(formatMessage({ id: 'receipts_list' }));
}, [changePageTitle, formatMessage]);
- // handle delete receipt click
- const handleDeleteReceipt = useCallback(
- ({ id }) => {
- openAlert('receipt-delete', { receiptId: id });
- },
- [openAlert],
- );
-
- // Handle cancel/confirm receipt deliver.
- const handleCloseReceipt = useCallback(({ id }) => {
- openAlert('receipt-close', { receiptId: id });
- }, []);
-
- // Handle filter change to re-fetch data-table.
- const handleFilterChanged = useCallback(() => {}, [fetchReceipts]);
-
- const handleEditReceipt = useCallback(
- (receipt) => {
- history.push(`/receipts/${receipt.id}/edit`);
- },
- [history],
- );
-
- const handleSelectedRowsChange = useCallback(
- (estimate) => {
- setSelectedRows(estimate);
- },
- [setSelectedRows],
- );
-
return (
-
+
-
-
-
-
-
-
-
+
+
+
-
+
);
}
export default compose(
- withResourceActions,
- withReceiptActions,
withDashboardActions,
- withViewsActions,
withReceipts(({ receiptTableQuery }) => ({
receiptTableQuery,
})),
- withAlertsActions,
)(ReceiptsList);
diff --git a/client/src/containers/Sales/Receipt/ReceiptsListProvider.js b/client/src/containers/Sales/Receipt/ReceiptsListProvider.js
new file mode 100644
index 000000000..c48618b99
--- /dev/null
+++ b/client/src/containers/Sales/Receipt/ReceiptsListProvider.js
@@ -0,0 +1,48 @@
+import React, { createContext } from 'react';
+import DashboardInsider from 'components/Dashboard/DashboardInsider';
+import { useResourceViews, useResourceFields, useReceipts } from 'hooks/query';
+
+
+const ReceiptsListContext = createContext();
+
+// Receipts list provider.
+function ReceiptsListProvider({ query, ...props }) {
+ // Fetch receipts resource views and fields.
+ const { data: receiptsViews, isFetching: isViewsLoading } = useResourceViews(
+ 'sale_receipt',
+ );
+
+ // Fetches the sale receipts resource fields.
+ // const {
+ // data: receiptsFields,
+ // isFetching: isFieldsLoading,
+ // } = useResourceFields('sale_receipt');
+
+ const {
+ data: { receipts, pagination },
+ isFetching: isReceiptsLoading,
+ } = useReceipts(query);
+
+ const provider = {
+ receipts,
+ pagination,
+ // receiptsFields,
+ receiptsViews,
+ isViewsLoading,
+ // isFieldsLoading,
+ isReceiptsLoading,
+ };
+
+ return (
+
+
+
+ );
+}
+
+const useReceiptsListContext = () => React.useContext(ReceiptsListContext);
+
+export { ReceiptsListProvider, useReceiptsListContext };
diff --git a/client/src/containers/Sales/Receipt/ReceiptsViewPage.js b/client/src/containers/Sales/Receipt/ReceiptsViewPage.js
new file mode 100644
index 000000000..e4cf60976
--- /dev/null
+++ b/client/src/containers/Sales/Receipt/ReceiptsViewPage.js
@@ -0,0 +1,66 @@
+import React, { useCallback } from 'react';
+import { Switch, Route, useHistory } from 'react-router-dom';
+
+import ReceiptViewTabs from './ReceiptViewTabs';
+
+import withAlertsActions from 'containers/Alert/withAlertActions';
+import withDialogActions from 'containers/Dialog/withDialogActions';
+
+import { compose } from 'utils';
+
+/**
+ * Sale receipts view page.
+ */
+function ReceiptsViewPage({
+ // #withAlertActions
+ openAlert,
+
+ // #withDialog.
+ openDialog,
+}) {
+ const history = useHistory();
+
+ // handle delete receipt click
+ const handleDeleteReceipt = useCallback(
+ ({ id }) => {
+ openAlert('receipt-delete', { receiptId: id });
+ },
+ [openAlert],
+ );
+
+ const handleSelectedRowsChange = useCallback((estimate) => {}, []);
+
+ const handleEditReceipt = useCallback(
+ (receipt) => {
+ history.push(`/receipts/${receipt.id}/edit`);
+ },
+ [history],
+ );
+
+ // Handle cancel/confirm receipt deliver.
+ const handleCloseReceipt = useCallback(
+ ({ id }) => {
+ openAlert('receipt-close', { receiptId: id });
+ },
+ [openAlert],
+ );
+
+ return (
+
+
+
+ {/* */}
+
+
+ );
+}
+
+export default compose(withAlertsActions, withDialogActions)(ReceiptsViewPage);
diff --git a/client/src/containers/Vendors/VendorActionsBar.js b/client/src/containers/Vendors/VendorActionsBar.js
index cae1e879c..49772e53b 100644
--- a/client/src/containers/Vendors/VendorActionsBar.js
+++ b/client/src/containers/Vendors/VendorActionsBar.js
@@ -1,4 +1,4 @@
-import React, { useCallback, useMemo, useState } from 'react';
+import React from 'react';
import {
NavbarGroup,
NavbarDivider,
@@ -11,42 +11,34 @@ import {
} from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl';
import classNames from 'classnames';
-import { connect } from 'react-redux';
-import { useHistory } from 'react-router-dom';
-import Icon from 'components/Icon';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
+import Icon from 'components/Icon';
import { If, DashboardActionViewsList } from 'components';
-import withVendors from './withVendors';
+import { useVendorsListContext } from './VendorsListProvider';
+import { useHistory } from 'react-router-dom';
+
import withVendorActions from './withVendorActions';
+
import { compose } from 'utils';
+/**
+ * Vendors actions bar.
+ */
function VendorActionsBar({
- // #withVendors
- vendorViews,
-
// #withVendorActions
addVendorsTableQueries,
- changeVendorView,
-
- // #ownProps
- selectedRows = [],
}) {
- const [filterCount, setFilterCount] = useState(0);
const history = useHistory();
const { formatMessage } = useIntl();
+ const { vendorsViews } = useVendorsListContext();
- const onClickNewVendor = useCallback(() => {
+ const onClickNewVendor = () => {
history.push('/vendors/new');
- }, [history]);
-
- const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
- selectedRows,
- ]);
+ };
const handleTabChange = (viewId) => {
- changeVendorView(viewId.id || -1);
addVendorsTableQueries({
custom_view_id: viewId.id || null,
});
@@ -57,7 +49,7 @@ function VendorActionsBar({
@@ -76,16 +68,16 @@ function VendorActionsBar({
) : (
- `${filterCount} ${formatMessage({ id: 'filters_applied' })}`
+ `${9} ${formatMessage({ id: 'filters_applied' })}`
)
}
icon={}
/>
-
+
}
@@ -107,13 +99,7 @@ function VendorActionsBar({
);
}
-const mapStateToProps = (state, props) => ({
- resourceName: 'vendors',
-});
-const withVendorsActionsBar = connect(mapStateToProps);
export default compose(
- withVendorsActionsBar,
withVendorActions,
- withVendors(({ vendorViews }) => ({ vendorViews })),
)(VendorActionsBar);
diff --git a/client/src/containers/Vendors/VendorFinanicalPanelTab.js b/client/src/containers/Vendors/VendorFinanicalPanelTab.js
index 5397f5b91..c5bfc64bd 100644
--- a/client/src/containers/Vendors/VendorFinanicalPanelTab.js
+++ b/client/src/containers/Vendors/VendorFinanicalPanelTab.js
@@ -12,26 +12,15 @@ import {
Col,
} from 'components';
import { FormattedMessage as T } from 'react-intl';
-
-import withCurrencies from 'containers/Currencies/withCurrencies';
-
-import {
- compose,
- momentFormatter,
- tansformDateValue,
- inputIntent,
-} from 'utils';
+import { momentFormatter, tansformDateValue, inputIntent } from 'utils';
+import { useVendorFormContext } from './VendorFormProvider';
/**
* Vendor Finaniceal Panel Tab.
*/
-function VendorFinanicalPanelTab({
- // #withCurrencies
- currenciesList,
+export default function VendorFinanicalPanelTab() {
+ const { vendorId, currencies } = useVendorFormContext();
- // #OwnProps
- vendorId,
-}) {
return (
@@ -104,7 +93,7 @@ function VendorFinanicalPanelTab({
inline={true}
>
{
form.setFieldValue('currency_code', currency.currency_code);
@@ -119,7 +108,3 @@ function VendorFinanicalPanelTab({
);
}
-
-export default compose(
- withCurrencies(({ currenciesList }) => ({ currenciesList })),
-)(VendorFinanicalPanelTab);
diff --git a/client/src/containers/Vendors/VendorFloatingActions.js b/client/src/containers/Vendors/VendorFloatingActions.js
index 04b01fdf7..757f01881 100644
--- a/client/src/containers/Vendors/VendorFloatingActions.js
+++ b/client/src/containers/Vendors/VendorFloatingActions.js
@@ -13,41 +13,43 @@ import { FormattedMessage as T } from 'react-intl';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import { useFormikContext } from 'formik';
-import { saveInvoke } from 'utils';
+import { useHistory } from 'react-router-dom';
import { Icon } from 'components';
+import { useVendorFormContext } from './VendorFormProvider';
/**
* Vendor floating actions bar.
*/
-export default function VendorFloatingActions({
- onSubmitClick,
- onSubmitAndNewClick,
- onCancelClick,
- isSubmitting,
- vendor,
-}) {
- const { resetForm, submitForm } = useFormikContext();
+export default function VendorFloatingActions() {
+ // Formik context.
+ const { resetForm, isSubmitting, submitForm } = useFormikContext();
+ // Vendor form context.
+ const { vendor, setSubmitPayload } = useVendorFormContext();
+
+ // History.
+ const history = useHistory();
+
+ // Handle the submit button.
const handleSubmitBtnClick = (event) => {
- saveInvoke(onSubmitClick, event, {
- noRedirect: false,
- });
- };
-
- const handleCancelBtnClick = (event) => {
- saveInvoke(onCancelClick, event);
- };
-
- const handleClearBtnClick = (event) => {
- // saveInvoke(onClearClick, event);
- resetForm();
+ setSubmitPayload({ noRedirect: false, });
+ submitForm();
};
+ // Handle the submit & new button click.
const handleSubmitAndNewClick = (event) => {
submitForm();
- saveInvoke(onSubmitClick, event, {
- noRedirect: true,
- });
+ setSubmitPayload({ noRedirect: true, });
+ };
+
+ // Handle cancel button click.
+ const handleCancelBtnClick = (event) => {
+ history.goBack();
+ };
+
+ // Handle clear button click.
+ const handleClearBtnClick = (event) => {
+ resetForm();
};
return (
diff --git a/client/src/containers/Vendors/VendorForm.js b/client/src/containers/Vendors/VendorForm.js
index 4533b6972..8c872e100 100644
--- a/client/src/containers/Vendors/VendorForm.js
+++ b/client/src/containers/Vendors/VendorForm.js
@@ -1,5 +1,4 @@
-import React, { useState, useMemo, useCallback, useEffect } from 'react';
-import * as Yup from 'yup';
+import React, { useMemo, useEffect } from 'react';
import { Formik, Form } from 'formik';
import moment from 'moment';
import { Intent } from '@blueprintjs/core';
@@ -20,11 +19,10 @@ import VendorTabs from './VendorsTabs';
import VendorFloatingActions from './VendorFloatingActions';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
-import withVendorDetail from './withVendorDetail';
-import withVendorActions from './withVendorActions';
import withSettings from 'containers/Settings/withSettings';
import { compose, transformToForm } from 'utils';
+import { useVendorFormContext } from './VendorFormProvider';
const defaultInitialValues = {
salutation: '',
@@ -68,20 +66,19 @@ function VendorForm({
// #withDashboardActions
changePageTitle,
- // #withVendorDetailsActions
- vendor,
- // #withVendorActions
- requestSubmitVendor,
- requestEditVendor,
-
// #withSettings
baseCurrency,
-
- // #OwnProps
- vendorId,
}) {
+ const {
+ vendorId,
+ vendor,
+ createVendorMutate,
+ editVendorMutate,
+ setSubmitPayload,
+ submitPayload,
+ } = useVendorFormContext();
+
const isNewMode = !vendorId;
- const [submitPayload, setSubmitPayload] = useState({});
const history = useHistory();
const { formatMessage } = useIntl();
@@ -95,15 +92,16 @@ function VendorForm({
currency_code: baseCurrency,
...transformToForm(vendor, defaultInitialValues),
}),
- [defaultInitialValues],
+ [vendor, baseCurrency],
);
+
useEffect(() => {
!isNewMode
? changePageTitle(formatMessage({ id: 'edit_vendor' }))
: changePageTitle(formatMessage({ id: 'new_vendor' }));
}, [changePageTitle, isNewMode, formatMessage]);
- //Handles the form submit.
+ // Handles the form submit.
const handleFormSubmit = (
values,
{ setSubmitting: resetForm, setErrors },
@@ -132,23 +130,12 @@ function VendorForm({
};
if (vendor && vendor.id) {
- requestEditVendor(vendor.id, requestForm).then(onSuccess).catch(onError);
+ editVendorMutate(vendor.id, requestForm).then(onSuccess).catch(onError);
} else {
- requestSubmitVendor(requestForm).then(onSuccess).catch(onError);
+ createVendorMutate(requestForm).then(onSuccess).catch(onError);
}
};
- const handleCancelClick = useCallback(() => {
- history.goBack();
- }, [history]);
-
- const handleSubmitClick = useCallback(
- (event, payload) => {
- setSubmitPayload({ ...payload });
- },
- [setSubmitPayload],
- );
-
return (
- {({ isSubmitting }) => (
-
- )}
+
+
);
}
export default compose(
- withVendorDetail(),
withDashboardActions,
withSettings(({ organizationSettings }) => ({
baseCurrency: organizationSettings?.baseCurrency,
})),
- withVendorActions,
)(VendorForm);
diff --git a/client/src/containers/Vendors/VendorFormAfterPrimarySection.js b/client/src/containers/Vendors/VendorFormAfterPrimarySection.js
index aa4a0763f..9e51bd550 100644
--- a/client/src/containers/Vendors/VendorFormAfterPrimarySection.js
+++ b/client/src/containers/Vendors/VendorFormAfterPrimarySection.js
@@ -2,8 +2,6 @@ import React from 'react';
import { FormGroup, InputGroup, ControlGroup } from '@blueprintjs/core';
import { FastField, ErrorMessage } from 'formik';
import { FormattedMessage as T } from 'react-intl';
-import classNames from 'classnames';
-import { CLASSES } from 'common/classes';
import { inputIntent } from 'utils';
/**
diff --git a/client/src/containers/Vendors/VendorFormPage.js b/client/src/containers/Vendors/VendorFormPage.js
index 51dd7eed8..0844145fb 100644
--- a/client/src/containers/Vendors/VendorFormPage.js
+++ b/client/src/containers/Vendors/VendorFormPage.js
@@ -1,68 +1,25 @@
-import React, { useCallback } from 'react';
+import React from 'react';
import { useParams, useHistory } from 'react-router-dom';
-import { useQuery } from 'react-query';
import { DashboardCard } from 'components';
import VendorFrom from './VendorForm';
-import DashboardInsider from 'components/Dashboard/DashboardInsider';
-import withVendorActions from './withVendorActions';
-import withCurrenciesActions from 'containers/Currencies/withCurrenciesActions';
+import { VendorFormProvider } from './VendorFormProvider';
-import { compose } from 'utils';
-
-function VendorFormPage({
- // #withVendorActions
- requestFetchVendorsTable,
- requestFetchVendor,
-
- // #withCurrenciesActions
- requestFetchCurrencies,
-}) {
+/**
+ * Vendor form page.
+ */
+function VendorFormPage() {
const { id } = useParams();
const history = useHistory();
-
- // Handle fetch Currencies data table
- const fetchCurrencies = useQuery('currencies', () =>
- requestFetchCurrencies(),
- );
-
- // Handle fetch vendors data table
- const fetchVendors = useQuery('vendor-list', () =>
- requestFetchVendorsTable({}),
- );
-
- // Handle fetch vendor details.
- const fetchVendor = useQuery(
- ['vendor', id],
- (_id, vendorId) => requestFetchVendor(vendorId),
- { enabled: id && id },
- );
-
- const handleFormSubmit = useCallback(() => {}, []);
-
- const handleCancel = useCallback(() => {
- history.goBack();
- }, [history]);
-
+
return (
-
+
-
+
-
+
);
}
-export default compose(withCurrenciesActions, withVendorActions)(VendorFormPage);
+export default VendorFormPage;
diff --git a/client/src/containers/Vendors/VendorFormProvider.js b/client/src/containers/Vendors/VendorFormProvider.js
new file mode 100644
index 000000000..ffeb61a3a
--- /dev/null
+++ b/client/src/containers/Vendors/VendorFormProvider.js
@@ -0,0 +1,61 @@
+import React, { useState, createContext } from 'react';
+import DashboardInsider from 'components/Dashboard/DashboardInsider';
+import {
+ useVendor,
+ useVendors,
+ useCurrencies,
+ useCreateVendor,
+ useEditVendor,
+} from 'hooks/query';
+
+const VendorFormContext = createContext();
+
+/**
+ * Vendor form provider.
+ */
+function VendorFormProvider({ vendorId, ...props }) {
+ // Handle fetch Currencies data table
+ const { data: currencies, isFetching: isCurrenciesLoading } = useCurrencies();
+
+ // Handle fetch vendors data table
+ const {
+ data: { vendors },
+ isFetching: isVendorsLoading,
+ } = useVendors();
+
+ // Handle fetch vendor details.
+ const { data: vendor, isFetching: isVendorLoading } = useVendor(vendorId, {
+ enabled: !!vendorId,
+ });
+
+ // Form submit payload.
+ const [submitPayload, setSubmitPayload] = useState({});
+
+ // Create and edit vendor mutations.
+ const { mutateAsync: createVendorMutate } = useCreateVendor();
+ const { mutateAsync: editVendorMutate } = useEditVendor();
+
+ const provider = {
+ currencies,
+ vendors,
+ vendor,
+ submitPayload,
+
+ createVendorMutate,
+ editVendorMutate,
+ setSubmitPayload,
+ };
+
+ return (
+
+
+
+ );
+}
+
+const useVendorFormContext = () => React.useContext(VendorFormContext);
+
+export { VendorFormProvider, useVendorFormContext };
diff --git a/client/src/containers/Vendors/VendorViewsTabs.js b/client/src/containers/Vendors/VendorViewsTabs.js
index ecba2f9e0..631805df7 100644
--- a/client/src/containers/Vendors/VendorViewsTabs.js
+++ b/client/src/containers/Vendors/VendorViewsTabs.js
@@ -1,54 +1,34 @@
-import React, { useEffect, useMemo } from 'react';
+import React, { useMemo } from 'react';
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
import { compose } from 'redux';
-import { useParams, withRouter, useHistory } from 'react-router-dom';
-import { connect } from 'react-redux';
+import { useParams } from 'react-router-dom';
+import { useVendorsListContext } from './VendorsListProvider';
import { DashboardViewsTabs } from 'components';
-import withVendors from './withVendors';
import withVendorActions from './withVendorActions';
-import withDashboardActions from 'containers/Dashboard/withDashboardActions';
-import withViewDetail from 'containers/Views/withViewDetails';
import { pick } from 'lodash';
/**
* Vendors views tabs.
*/
function VendorViewsTabs({
- // #withViewDetail
- viewId,
- viewItem,
-
- // #withVendors
- vendorViews,
-
// #withVendorActions
addVendorsTableQueries,
- changeVendorView,
-
- // #withDashboardActions
- setTopbarEditView,
- changePageSubtitle,
}) {
const { custom_view_id: customViewId = null } = useParams();
-
- useEffect(() => {
- changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
- setTopbarEditView(customViewId);
- }, [customViewId]);
-
+ const { vendorsViews } = useVendorsListContext();
+
const tabs = useMemo(() =>
- vendorViews.map(
+ vendorsViews.map(
(view) => ({
...pick(view, ['name', 'id']),
}),
- [vendorViews],
),
+ [vendorsViews],
);
const handleTabsChange = (viewId) => {
- changeVendorView(viewId || -1);
addVendorsTableQueries({
custom_view_id: viewId || null,
});
@@ -68,18 +48,6 @@ function VendorViewsTabs({
);
}
-const mapStateToProps = (state, ownProps) => ({
- viewId: ownProps.match.params.custom_view_id,
-});
-
-
-const withVendorsViewsTabs = connect(mapStateToProps);
-
export default compose(
- withRouter,
- withDashboardActions,
- withVendorsViewsTabs,
withVendorActions,
- withViewDetail(),
- withVendors(({ vendorViews }) => ({ vendorViews })),
)(VendorViewsTabs);
diff --git a/client/src/containers/Vendors/VendorsList.js b/client/src/containers/Vendors/VendorsList.js
index df5de3481..06b6257b3 100644
--- a/client/src/containers/Vendors/VendorsList.js
+++ b/client/src/containers/Vendors/VendorsList.js
@@ -1,79 +1,47 @@
-import React, { useEffect, useState } from 'react';
-import { useQuery } from 'react-query';
-import { FormattedMessage as T, useIntl } from 'react-intl';
+import React, { useEffect } from 'react';
+import { useIntl } from 'react-intl';
-import DashboardInsider from 'components/Dashboard/DashboardInsider';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
+import { VendorsListProvider } from './VendorsListProvider';
import VendorActionsBar from 'containers/Vendors/VendorActionsBar';
import VendorsViewPage from 'containers/Vendors/VendorsViewPage';
import VendorsAlerts from 'containers/Vendors/VendorsAlerts';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
-import withResourceActions from 'containers/Resources/withResourcesActions';
-import withViewsActions from 'containers/Views/withViewsActions';
import withVendors from 'containers/Vendors/withVendors';
-import withVendorActions from 'containers/Vendors/withVendorActions';
import { compose } from 'utils';
+/**
+ * Vendors list page.
+ */
function VendorsList({
// #withDashboardActions
changePageTitle,
- // #withResourceActions
- requestFetchResourceViews,
-
// #withVendors
vendorTableQuery,
-
- // #withVendorActions
- requestFetchVendorsTable,
}) {
- const [tableLoading, setTableLoading] = useState(false);
-
const { formatMessage } = useIntl();
useEffect(() => {
changePageTitle(formatMessage({ id: 'vendors_list' }));
}, [changePageTitle, formatMessage]);
- // Fetch vendors resource views and fields.
- const fetchResourceViews = useQuery(
- ['resource-views', 'vendors'],
- (key, resourceName) => requestFetchResourceViews(resourceName),
- );
-
- // Handle fetch vendors data table
- const fetchVendors = useQuery(
- ['vendors-table', vendorTableQuery],
- (key, query) => requestFetchVendorsTable({ ...query }),
- );
-
- useEffect(() => {
- if (tableLoading && !fetchVendors.isFetching) {
- setTableLoading(false);
- }
- }, [tableLoading, fetchVendors.isFetching]);
-
return (
-
+
+
-
-
+
+ {/* */}
-
+
);
}
export default compose(
- withResourceActions,
- withVendorActions,
withDashboardActions,
- withViewsActions,
withVendors(({ vendorTableQuery }) => ({ vendorTableQuery })),
)(VendorsList);
diff --git a/client/src/containers/Vendors/VendorsListProvider.js b/client/src/containers/Vendors/VendorsListProvider.js
new file mode 100644
index 000000000..2f1757b6b
--- /dev/null
+++ b/client/src/containers/Vendors/VendorsListProvider.js
@@ -0,0 +1,39 @@
+import React, { createContext } from 'react';
+
+import DashboardInsider from 'components/Dashboard/DashboardInsider';
+import { useResourceViews, useVendors } from 'hooks/query';
+
+const VendorsListContext = createContext();
+
+function VendorsListProvider({ query, ...props }) {
+
+ // Fetch vendors list with pagination meta.
+ const {
+ data: { vendors, pagination },
+ isFetching: isVendorsLoading,
+ } = useVendors(query);
+
+ // Fetch customers resource views and fields.
+ const {
+ data: vendorsViews,
+ isFetching: isVendorsViewsLoading,
+ } = useResourceViews('vendors');
+
+ const provider = {
+ vendors,
+ pagination,
+ vendorsViews,
+ isVendorsLoading,
+ isVendorsViewsLoading,
+ };
+
+ return (
+
+
+
+ );
+}
+
+const useVendorsListContext = () => React.useContext(VendorsListContext);
+
+export { VendorsListProvider, useVendorsListContext };
diff --git a/client/src/containers/Vendors/VendorsTable.js b/client/src/containers/Vendors/VendorsTable.js
index 195a5b457..01fa50603 100644
--- a/client/src/containers/Vendors/VendorsTable.js
+++ b/client/src/containers/Vendors/VendorsTable.js
@@ -1,4 +1,4 @@
-import React, { useRef, useEffect, useCallback, useMemo } from 'react';
+import React, { useCallback, useMemo } from 'react';
import {
Button,
Popover,
@@ -8,15 +8,15 @@ import {
Position,
Intent,
} from '@blueprintjs/core';
-import { FormattedMessage as T, useIntl } from 'react-intl';
+import { useIntl } from 'react-intl';
import classNames from 'classnames';
-import { useIsValuePassed } from 'hooks';
-import VendorsEmptyStatus from './VendorsEmptyStatus';
-import { DataTable, LoadingIndicator, Icon, Money, Choose } from 'components';
import { CLASSES } from 'common/classes';
-import withVendors from './withVendors';
+import VendorsEmptyStatus from './VendorsEmptyStatus';
+import { DataTable, Icon, Money, Choose } from 'components';
+
+import { useVendorsListContext } from './VendorsListProvider';
import withVendorsActions from './withVendorActions';
import { compose, firstLettersArgs, saveInvoke } from 'utils';
@@ -26,25 +26,16 @@ const AvatarCell = (row) => {
};
function VendorsTable({
- // #withVendors
- vendorsCurrentPage,
- vendorsLoading,
- vendorsPageination,
- vendorTableQuery,
- vendorItems,
- vendorsCurrentViewId,
-
// #withVendorsActions
addVendorsTableQueries,
// #ownProps
- loading,
onEditVendor,
onDeleteVendor,
onSelectedRowsChange,
}) {
const { formatMessage } = useIntl();
- const isLoadedBefore = useIsValuePassed(loading, false);
+ const { vendors } = useVendorsListContext();
// Vendor actions list.
const renderContextMenu = useMemo(
@@ -186,64 +177,38 @@ function VendorsTable({
onEditVendor,
onDeleteVendor,
});
- const showEmptyStatus = [
- vendorsCurrentViewId === -1,
- vendorItems.length === 0,
- ].every((condition) => condition === true);
return (
-
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
);
}
-export default compose(
- withVendors(
- ({
- vendorItems,
- vendorsLoading,
- vendorTableQuery,
- vendorsPageination,
- vendorsCurrentViewId,
- }) => ({
- vendorItems,
- vendorsLoading,
- vendorsPageination,
- vendorTableQuery,
- vendorsCurrentViewId,
- }),
- ),
- withVendorsActions,
-)(VendorsTable);
+export default compose(withVendorsActions)(VendorsTable);
diff --git a/client/src/containers/Vendors/VendorsTabs.js b/client/src/containers/Vendors/VendorsTabs.js
index 9e96b2991..02c75c030 100644
--- a/client/src/containers/Vendors/VendorsTabs.js
+++ b/client/src/containers/Vendors/VendorsTabs.js
@@ -8,7 +8,7 @@ import VendorAttahmentTab from './VendorAttahmentTab';
import CustomerAddressTabs from 'containers/Customers/CustomerAddressTabs';
import CustomerNotePanel from 'containers/Customers/CustomerNotePanel';
-export default function VendorTabs({ vendor }) {
+export default function VendorTabs() {
const { formatMessage } = useIntl();
return (
@@ -21,7 +21,7 @@ export default function VendorTabs({ vendor }) {
}
+ panel={
}
/>
{
+ return response.data.account;
+};
+
+/**
+ * Retrieve accounts list.
+ */
+export function useAccounts(query, props) {
+ return useQuery(
+ ['ACCOUNTS', query],
+ () =>
+ ApiService.get('accounts', { params: query }).then(
+ (response) => response.data.accounts,
+ ),
+ {
+ initialData: [],
+ ...props
+ },
+ );
+}
+
+/**
+ * Retrieve the given account details.
+ * @param {number} id -
+ */
+export function useAccount(id, props) {
+ return useQuery(
+ ['ACCOUNT', id],
+ () => ApiService.get(`accounts/${id}`).then(transformAccount),
+ {
+ initialData: {},
+ ...props,
+ },
+ );
+}
+
+/**
+ * Retrieve accounts types list.
+ */
+export function useAccountsTypes() {
+ return useQuery(['ACCOUNTS_TYPES'], () => ApiService.get('account_types'), {
+ initialData: [],
+ });
+}
+
+/**
+ * Creates account.
+ */
+export function useCreateAccount(props) {
+ const client = useQueryClient();
+
+ return useMutation((values) => ApiService.post('accounts', values), {
+ onSuccess: () => {
+ client.invalidateQueries('ACCOUNTS');
+ },
+ ...props
+ });
+}
+
+/**
+ * Edits the given account.
+ */
+export function useEditAccount(props) {
+ const query = useQueryClient();
+
+ return useMutation(
+ (values, id) => ApiService.post(`accounts/${id}`, values),
+ {
+ onSuccess: () => {
+ query.invalidateQueries('ACCOUNTS');
+ },
+ ...props
+ },
+ );
+}
+
+/**
+ * Edits the given account.
+ */
+export function useDeleteAccount(props) {
+ const query = useQueryClient();
+
+ return useMutation(
+ (id) =>
+ ApiService.delete(`accounts/${id}`).catch((error) => {
+ throw new Error(error.response.data);
+ }),
+ {
+ onSuccess: () => {
+ query.invalidateQueries('ACCOUNTS');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Actiavte the give account.
+ */
+export function useActivateAccount(props) {
+ const query = useQueryClient();
+
+ return useMutation(
+ (id) => ApiService.post(`accounts/${id}/activate`),
+ {
+ onSuccess: () => {
+
+ },
+ ...props
+ }
+ );
+}
+
+/**
+ * Inactivate the given account.
+ */
+export function useInactivateAccount(props) {
+ const query = useQueryClient();
+
+ return useMutation(
+ (id) => ApiService.post(`accounts/${id}/inactivate`),
+ {
+ onSuccess: () => {
+
+ },
+ ...props
+ },
+ );
+}
diff --git a/client/src/hooks/query/bills.js b/client/src/hooks/query/bills.js
new file mode 100644
index 000000000..998b6aebd
--- /dev/null
+++ b/client/src/hooks/query/bills.js
@@ -0,0 +1,115 @@
+import { useQueryClient, useQuery, useMutation } from 'react-query';
+import ApiService from 'services/ApiService';
+
+// Bills transformer.
+const billsTransformer = (response) => {
+ return {
+ bills: response.data.bills,
+ pagination: response.data.pagination,
+ filterMeta: response.data.filter_meta,
+ };
+};
+
+/**
+ * Creates a new sale invoice.
+ */
+export function useCreateBill(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation((values) => ApiService.post('purchases/bills', values), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('BILLS');
+ },
+ ...props,
+ });
+}
+
+/**
+ * Edits the given sale invoice.
+ */
+export function useEditBill(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ (id, values) => ApiService.post(`purchases/bills/${id}`, values),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries('BILLS');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Deletes the given sale invoice.
+ */
+export function useDeleteBill(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation((id) => ApiService.delete(`purchases/bills/${id}`), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('BILLS');
+ },
+ ...props,
+ });
+}
+
+/**
+ * Retrieve sale invoices list with pagination meta.
+ */
+export function useBills(query, props) {
+ return useQuery(
+ ['BILLS', query],
+ () =>
+ ApiService.get('purchases/bills', { params: query }).then(
+ billsTransformer,
+ ),
+ {
+ initialData: {
+ bills: [],
+ pagination: {
+ page: 1,
+ page_size: 12,
+ total: 0,
+ },
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Retrieve bill details of the given bill id.
+ * @param {number} id - Bill id.
+ */
+export function useBill(id, props) {
+ return useQuery(
+ ['BILL', id],
+ async () => {
+ const { data } = await ApiService.get(`/purchases/bills/${id}`);
+ return data.bill;
+ },
+ {
+ initialData: {},
+ ...props,
+ },
+ );
+}
+
+/**
+ * Marks the given bill as open.
+ */
+export function useOpenBill(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ (id) => ApiService.delete(`purchases/bills/${id}/open`),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries('BILLS');
+ },
+ ...props,
+ },
+ );
+}
diff --git a/client/src/hooks/query/currencies.js b/client/src/hooks/query/currencies.js
new file mode 100644
index 000000000..37aab5782
--- /dev/null
+++ b/client/src/hooks/query/currencies.js
@@ -0,0 +1,41 @@
+import { useMutation, useQuery } from 'react-query';
+import ApiService from 'services/ApiService';
+
+/**
+ * Create a new currency.
+ */
+export function useCreateCurrency() {
+ return useMutation((values) => ApiService.post('currencies', values));
+}
+
+/**
+ * Edits the given currency code.
+ */
+export function useEditCurrency() {
+ return useMutation((currencyCode, values) =>
+ ApiService.post(`currencies/${currencyCode}`, values),
+ );
+}
+
+/**
+ * Deletes the given currency.
+ */
+export function useDeleteCurrency() {
+ return useMutation((currencyCode) =>
+ ApiService.delete(`currencies/${currencyCode}`),
+ );
+}
+
+/**
+ * Retrieve the currencies list.
+ */
+export function useCurrencies(props) {
+ return useQuery(
+ ['CURRENCIES'],
+ () => ApiService.get('currencies').then(res => res.data.currencies),
+ {
+ initialData: [],
+ ...props,
+ },
+ );
+}
diff --git a/client/src/hooks/query/customers.js b/client/src/hooks/query/customers.js
new file mode 100644
index 000000000..9078e20eb
--- /dev/null
+++ b/client/src/hooks/query/customers.js
@@ -0,0 +1,72 @@
+
+import { useMutation, useQuery } from 'react-query';
+import ApiService from 'services/ApiService';
+
+
+const transformCustomers = (response) => {
+ return response.data;
+}
+
+const transformCustomer = (response) => {
+ return response.data;
+}
+
+/**
+ *
+ * @param {*} query
+ * @param {*} props
+ */
+export function useCustomers(query, props) {
+ return useQuery(
+ ['CUSTOMERS', query],
+ () => ApiService
+ .get(`customers`, { params: query })
+ .then(transformCustomers),
+ {
+ initialData: {
+ customers: [],
+ pagination: {},
+ },
+ ...props
+ }
+ )
+}
+
+/**
+ *
+ * @param {*} props
+ */
+export function useEditCustomer(props) {
+ return useMutation(
+ (values, id) => ApiService.post(`customers/${id}`, values),
+ props
+ );
+}
+
+/**
+ *
+ * @param {*} props
+ */
+export function useDeleteCustomer(props) {
+ return useMutation(
+ (id) => ApiService.delete(`customers/${id}`),
+ props
+ );
+}
+
+export function useCreateCustomer(props) {
+ return useMutation(
+ (values) => ApiService.post('customers', values),
+ props
+ );
+}
+
+export function useCustomer(id, props) {
+ return useQuery(
+ ['CUSTOMER', id],
+ () => ApiService
+ .get(`customers/${id}`)
+ .then(transformCustomer),
+ props
+ )
+};
\ No newline at end of file
diff --git a/client/src/hooks/query/estimates.js b/client/src/hooks/query/estimates.js
new file mode 100644
index 000000000..eb2d3e7b9
--- /dev/null
+++ b/client/src/hooks/query/estimates.js
@@ -0,0 +1,129 @@
+import { useQueryClient, useQuery, useMutation } from 'react-query';
+import ApiService from 'services/ApiService';
+
+const invoicesTransformer = (response) => {
+ return {
+ estimates: response.data.sale_invoices,
+ pagination: response.data.pagination,
+ };
+};
+
+/**
+ * Creates a new sale estimate.
+ */
+export function useCreateEstimate(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation((values) => ApiService.post('sales/estimates', values), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SALE_ESTIMATES');
+ },
+ ...props,
+ });
+}
+
+/**
+ * Edits the given sale estimate.
+ */
+export function useEditEstimate(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ (id, values) => ApiService.post(`sales/estimates/${id}`, values),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SALE_ESTIMATES');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Deletes the given sale invoice.
+ */
+export function useDeleteEstimate(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation((id) => ApiService.delete(`sales/estimates/${id}`), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SALE_ESTIMATES');
+ },
+ ...props,
+ });
+}
+
+/**
+ * Retrieve sale invoices list with pagination meta.
+ */
+export function useEstimates(query, props) {
+ return useQuery(
+ ['SALE_INVOICES', query],
+ () =>
+ ApiService.get('sales/estimates', { params: query }).then(
+ invoicesTransformer,
+ ),
+ {
+ initialData: {
+ saleEstimates: [],
+ pagination: {
+ page: 1,
+ page_size: 12,
+ total: 0,
+ },
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Mark the given estimate as delivered.
+ */
+export function useDeliverEstimate(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ (id) => ApiService.delete(`sales/estimates/${id}/deliver`),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SALE_ESTIMATES');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Mark the given estimate as approved.
+ */
+export function useApproveEstimate(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ (id) => ApiService.delete(`sales/estimates/${id}/deliver`),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SALE_ESTIMATES');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Mark the given estimate as rejected.
+ */
+export function useRejectEstimate(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ (id) => ApiService.delete(`sales/estimates/${id}/reject`),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SALE_ESTIMATES');
+ },
+ ...props,
+ },
+ );
+}
diff --git a/client/src/hooks/query/expenses.js b/client/src/hooks/query/expenses.js
new file mode 100644
index 000000000..c6e13d57a
--- /dev/null
+++ b/client/src/hooks/query/expenses.js
@@ -0,0 +1,42 @@
+import { useQuery, useMutation } from 'react-query';
+import ApiService from 'services/ApiService';
+
+
+export function useExpenses(props) {
+ return useQuery(
+ ['EXPENSES'],
+ () => ApiService.get(`expenses`).then((response) => response.data),
+ {
+ initialData: {
+ expenses: [],
+ pagination: {},
+ },
+ ...props
+ },
+ );
+}
+
+export function useExpense(id, props) {
+ return useQuery(
+ ['EXPENSES', id],
+ () => ApiService.get(`expenses`).then((response) => response.data.expense),
+ {
+ initialData: {},
+ ...props
+ },
+ );
+}
+
+export function useDeleteExpense(props) {
+ return useMutation((id) => ApiService.delete(`expenses/${id}`), props);
+}
+
+export function useEditExpense(props) {
+ return useMutation((id, values) => ApiService.post(`expenses/${id}`, values), props);
+}
+
+
+export function useCreateExpense(props) {
+ return useMutation((values) => ApiService.post('expenses', values), props);
+}
+
\ No newline at end of file
diff --git a/client/src/hooks/query/financialReports.js b/client/src/hooks/query/financialReports.js
new file mode 100644
index 000000000..8bfd01fd8
--- /dev/null
+++ b/client/src/hooks/query/financialReports.js
@@ -0,0 +1,147 @@
+import { useQuery } from 'react-query';
+import ApiService from 'services/ApiService';
+import {
+ trialBalanceSheetReducer,
+ balanceSheetRowsReducer,
+ profitLossSheetReducer,
+ generalLedgerTableRowsReducer,
+ journalTableRowsReducer,
+} from 'containers/FinancialStatements/reducers';
+
+const transformBalanceSheet = (response) => {
+ return {
+ tableRows: balanceSheetRowsReducer(response.data.data),
+ ...response.data,
+ };
+};
+
+const transformTrialBalance = (response) => {
+ return {
+ tableRows: trialBalanceSheetReducer(response.data.data),
+ ...response.data,
+ };
+};
+
+const transformProfitLoss = (response) => {
+ return {
+ tableRows: profitLossSheetReducer(response.data.data),
+ ...response.data,
+ };
+};
+
+const transformGeneralLedger = (response) => {
+ return {
+ tableRows: generalLedgerTableRowsReducer(response.data.data),
+ ...response.data,
+ };
+};
+
+const transformJournal = (response) => {
+ return {
+ tableRows: journalTableRowsReducer(response.data.data),
+ ...response.data,
+ };
+};
+
+export function useBalanceSheet(query, props) {
+ return useQuery(
+ ['FINANCIAL-REPORT', 'BALANCE-SHEET', query],
+ () =>
+ ApiService.get('/financial_statements/balance_sheet', {
+ params: query,
+ }).then(transformBalanceSheet),
+ {
+ initialData: {
+ data: [],
+ columns: [],
+ query: {},
+ tableRows: [],
+ },
+ ...props,
+ },
+ );
+}
+
+export function useTrialBalanceSheet(query, props) {
+ return useQuery(
+ ['FINANCIAL-REPORT', 'TRIAL-BALANCE-SHEET', query],
+ () =>
+ ApiService.get('/financial_statements/trial_balance_sheet', {
+ params: query,
+ }).then(transformTrialBalance),
+ {
+ initialData: {
+ tableRows: [],
+ data: [],
+ query: {},
+ },
+ ...props,
+ },
+ );
+}
+
+export function useProfitLossSheet(query, props) {
+ return useQuery(
+ ['FINANCIAL-REPORT', 'PROFIT-LOSS-SHEET', query],
+ () =>
+ ApiService.get('/financial_statements/profit_loss_sheet', {
+ params: query,
+ }).then(transformProfitLoss),
+ {
+ initialData: {
+ data: {},
+ tableRows: [],
+ columns: [],
+ query: {},
+ },
+ ...props,
+ },
+ );
+}
+
+export function useGeneralLedgerSheet(query, props) {
+ return useQuery(
+ ['FINANCIAL-REPORT', 'GENERAL-LEDGER', query],
+ () =>
+ ApiService.get('/financial_statements/general_ledger', {
+ params: query,
+ }).then(transformGeneralLedger),
+ {
+ initialData: {
+ tableRows: [],
+ data: {},
+ query: {}
+ },
+ ...props
+ },
+ );
+}
+
+export function useJournalSheet(query, props) {
+ return useQuery(
+ ['FINANCIAL-REPORT', 'JOURNAL', query],
+ () =>
+ ApiService.get('/financial_statements/journal', { params: query }).then(
+ transformJournal,
+ ),
+ {
+ initialData: {
+ data: {},
+ tableRows: [],
+ query: {},
+ },
+ ...props
+ },
+ );
+}
+
+export function useARAgingSummaryReport(query, props) {
+ return useQuery(
+ ['FINANCIAL-REPORT', 'AR-AGING-SUMMARY', query],
+ () =>
+ ApiService.get('/financial_statements/receivable_aging_summary', {
+ params: query,
+ }),
+ props,
+ );
+}
diff --git a/client/src/hooks/query/index.js b/client/src/hooks/query/index.js
new file mode 100644
index 000000000..937a0539b
--- /dev/null
+++ b/client/src/hooks/query/index.js
@@ -0,0 +1,19 @@
+
+export * from './accounts';
+export * from './views';
+export * from './items';
+export * from './itemsCategories';
+export * from './inventoryAdjustments';
+export * from './expenses';
+export * from './financialReports';
+export * from './customers';
+export * from './vendors';
+export * from './manualJournals';
+export * from './currencies';
+export * from './invoices';
+export * from './bills';
+export * from './estimates';
+export * from './receipts';
+export * from './paymentReceives';
+export * from './paymentMades';
+export * from './settings';
\ No newline at end of file
diff --git a/client/src/hooks/query/inventoryAdjustments.js b/client/src/hooks/query/inventoryAdjustments.js
new file mode 100644
index 000000000..e398694d7
--- /dev/null
+++ b/client/src/hooks/query/inventoryAdjustments.js
@@ -0,0 +1,65 @@
+import { useMutation, useQuery, useQueryClient } from 'react-query';
+import ApiService from 'services/ApiService';
+
+/**
+ * Creates the inventory adjustment to the given item.
+ */
+export function useCreateInventoryAdjustment(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ (values) => ApiService.post('inventory_adjustments/quick', values),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries('INVENTORY_ADJUSTMENTS');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Deletes the inventory adjustment transaction.
+ */
+export function useDeleteInventoryAdjustment(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ (id) => ApiService.delete(`inventory_adjustments/${id}`),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries('INVENTORY_ADJUSTMENTS');
+ },
+ ...props
+ },
+ );
+}
+
+const inventoryAdjustmentsTransformer = (response) => {
+ return {
+ transactions: response.data.inventoy_adjustments,
+ pagination: response.data.pagination,
+ };
+}
+
+/**
+ * Retrieve inventory adjustment list with pagination meta.
+ */
+export function useInventoryAdjustments(query, props) {
+ return useQuery(
+ ['INVENTORY_ADJUSTMENTS', query],
+ () => ApiService.get('inventory_adjustments', { params: query })
+ .then(inventoryAdjustmentsTransformer),
+ {
+ initialData: {
+ transactions: [],
+ pagination: {
+ page: 1,
+ page_size: 12,
+ total: 0
+ },
+ },
+ ...props,
+ },
+ );
+}
\ No newline at end of file
diff --git a/client/src/hooks/query/invoices.js b/client/src/hooks/query/invoices.js
new file mode 100644
index 000000000..213b923d2
--- /dev/null
+++ b/client/src/hooks/query/invoices.js
@@ -0,0 +1,115 @@
+import { useQueryClient, useQuery, useMutation } from 'react-query';
+import ApiService from 'services/ApiService';
+
+// Invoices transformer.
+const invoicesTransformer = (response) => {
+ return {
+ invoices: response.data.sales_invoices,
+ pagination: response.data.pagination,
+ filterMeta: response.data.filter_meta,
+ };
+};
+
+const invoiceTransformer = (response) => {
+ return {
+ invoice: response.data.invoice,
+ }
+}
+/**
+ * Creates a new sale invoice.
+ */
+export function useCreateInvoice(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation((values) => ApiService.post('sales/invoices', values), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SALE_INVOICES');
+ },
+ ...props,
+ });
+}
+
+/**
+ * Edits the given sale invoice.
+ */
+export function useEditInvoice(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ (id, values) => ApiService.post(`sales/invoices/${id}`, values),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SALE_INVOICES');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Deletes the given sale invoice.
+ */
+export function useDeleteInvoice(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation((id) => ApiService.delete(`sales/invoices/${id}`), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SALE_INVOICES');
+ },
+ ...props,
+ });
+}
+
+/**
+ * Retrieve sale invoices list with pagination meta.
+ */
+export function useInvoices(query, props) {
+ return useQuery(
+ ['SALE_INVOICES', query],
+ () =>
+ ApiService.get('sales/invoices', { params: query }).then(
+ invoicesTransformer,
+ ),
+ {
+ initialData: {
+ saleInvoices: [],
+ pagination: {
+ page: 1,
+ page_size: 12,
+ total: 0,
+ },
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Marks the sale invoice as delivered.
+ */
+export function useDeliverInvoice(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ (id) => ApiService.delete(`sales/invoices/${id}/deliver`),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SALE_INVOICES');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Retrieve the sale invoice details.
+ */
+export function useInvoice(id, props) {
+ return useQuery(['SALE_INVOICE', id], () =>
+ ApiService.get(`sales/invoices/${id}`).then(invoiceTransformer),
+ {
+ initialData: {},
+ ...props
+ }
+ );
+}
diff --git a/client/src/hooks/query/items.js b/client/src/hooks/query/items.js
new file mode 100644
index 000000000..c64326d34
--- /dev/null
+++ b/client/src/hooks/query/items.js
@@ -0,0 +1,116 @@
+import { useMutation, useQuery, useQueryClient } from 'react-query';
+import ApiService from 'services/ApiService';
+import { transformResponse } from 'utils';
+
+const defaultPagination = {
+ pageSize: 12,
+ page: 0,
+ pagesCount: 0,
+};
+
+/**
+ * Creates a new item.
+ */
+export function useCreateItem(props) {
+ return useMutation((values) => ApiService.post('items', values), props);
+}
+
+/**
+ * Edits the given item.
+ */
+export function useEditItem(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(([id, values]) => ApiService.post(`items/${id}`, values), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('ITEMS');
+ queryClient.invalidateQueries('ITEM');
+ },
+ ...props,
+ });
+}
+
+/**
+ * Deletes the given item.
+ */
+export function useDeleteItem(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation((id) => ApiService.delete(`items/${id}`), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('ITEMS');
+ queryClient.invalidateQueries('ITEM');
+ },
+ ...props,
+ });
+}
+
+// Transformes items response.
+const transformItemsResponse = (response) => {
+ return {
+ items: response.data.items,
+ pagination: transformResponse(response.data.pagination),
+ filterMeta: transformResponse(response.data.filter_meta),
+ };
+};
+
+/**
+ * Retrieves items list.
+ */
+export function useItems(query, props) {
+ return useQuery(
+ ['ITEMS', query],
+ () =>
+ ApiService.get(`items`, { params: query }).then(transformItemsResponse),
+ {
+ initialData: {
+ items: [],
+ pagination: defaultPagination,
+ filterMeta: {},
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Retrieve details of the given item.
+ * @param {number} id - Item id.
+ */
+export function useItem(id, props) {
+ return useQuery(
+ ['ITEM', id],
+ () => ApiService.get(`items/${id}`).then((response) => response.data.item),
+ props,
+ );
+}
+
+/**
+ * Activate the given item.
+ */
+export function useActivateItem(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation((id) => ApiService.post(`items/${id}/activate`), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('ITEMS');
+ queryClient.invalidateQueries('ITEM');
+ },
+ ...props,
+ });
+}
+
+/**
+ * Inactivate the given item.
+ */
+export function useInactivateItem(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation((id) => ApiService.post(`items/${id}/inactivate`), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('ITEMS');
+ queryClient.invalidateQueries('ITEM');
+ },
+ ...props,
+ });
+}
diff --git a/client/src/hooks/query/itemsCategories.js b/client/src/hooks/query/itemsCategories.js
new file mode 100644
index 000000000..ee2f7daae
--- /dev/null
+++ b/client/src/hooks/query/itemsCategories.js
@@ -0,0 +1,94 @@
+import { useQuery, useMutation, useQueryClient } from 'react-query';
+import ApiService from 'services/ApiService';
+
+/**
+ * Creates a new item category.
+ */
+export function useCreateItemCategory(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ (values) => ApiService.post('item_categories', values),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries('ITEMS_CATEGORIES');
+ },
+ ...props
+ }
+ );
+}
+
+/**
+ * Edits the item category.
+ */
+export function useEditItemCategory(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ ([id, values]) => ApiService.post(`item_categories/${id}`, values),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries('ITEMS_CATEGORIES');
+ },
+ ...props
+ }
+ );
+}
+
+/**
+ * Deletes the given item category.
+ */
+export function useDeleteItemCategory(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ (id) => ApiService.delete(`item_categories/${id}`),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries('ITEMS_CATEGORIES');
+ },
+ ...props
+ });
+}
+
+// Transforms items categories.
+const transformItemsCategories = (response) => {
+ return {
+ itemsCategories: response.data.item_categories,
+ pagination: response.data.pagination,
+ };
+};
+
+/**
+ * Retrieve the items categories.
+ */
+export function useItemsCategories(query, props) {
+ return useQuery(
+ ['ITEMS_CATEGORIES', query],
+ () => ApiService.get(`item_categories`, { params: query })
+ .then(transformItemsCategories),
+ {
+ initialData: {
+ itemsCategories: [],
+ pagination: {}
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Retrieve the item category details.
+ * @param {number} id - Item category.
+ */
+export function useItemCategory(id, props) {
+ return useQuery(
+ ['ITEMS_CATEGORY', id],
+ () => ApiService.get(`item_categories/${id}`)
+ .then(res => res.data.category),
+ {
+ initialData: {},
+ ...props,
+ },
+ );
+}
\ No newline at end of file
diff --git a/client/src/hooks/query/manualJournals.js b/client/src/hooks/query/manualJournals.js
new file mode 100644
index 000000000..97193e6a0
--- /dev/null
+++ b/client/src/hooks/query/manualJournals.js
@@ -0,0 +1,87 @@
+import { useMutation, useQuery } from 'react-query';
+import ApiService from 'services/ApiService';
+
+// Transform joiurn
+const transformJournals = (response) => {
+ return {
+ manualJournals: response.data.manual_journals,
+ pagination: response.data.pagination,
+ };
+};
+
+/**
+ * Creates a new manual journal.
+ */
+export function useCreateJournal(props) {
+ return useMutation(
+ (values) => ApiService.post('manual-journals', values),
+ props,
+ );
+}
+
+/**
+ * Edits the given manual journal.
+ */
+export function useEditJournal(props) {
+ return useMutation(
+ (values, id) => ApiService.post(`manual-journals/${id}`, values),
+ props,
+ );
+}
+
+/**
+ * Deletes the given manual jouranl.
+ */
+export function useDeleteJournal(props) {
+ return useMutation(
+ (values, id) => ApiService.delete(`manual-journals/${id}`),
+ props,
+ );
+}
+
+/**
+ * Publishes the given manual journal.
+ */
+export function usePublishJournal(props) {
+ return useMutation(
+ (id) => ApiService.post(`manual-journals/${id}/publish`),
+ props,
+ );
+}
+
+/**
+ * Retrieve the manual journals with pagination meta.
+ */
+export function useJournals(query, props) {
+ return useQuery(
+ ['JOURNALS', query],
+ () =>
+ ApiService.get('manual-journals', { params: query }).then(
+ transformJournals,
+ ),
+ {
+ initialData: {
+ manualJournals: [],
+ pagination: {},
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Retrieve the manual journal details.
+ */
+export function useJournal(id, props) {
+ return useQuery(
+ ['JOURNAL', id],
+ async () => {
+ const { data } = await ApiService.get(`manual-journals/${id}`);
+
+ return data.manual_journal;
+ },
+ {
+ ...props,
+ },
+ );
+}
diff --git a/client/src/hooks/query/paymentMades.js b/client/src/hooks/query/paymentMades.js
new file mode 100644
index 000000000..20b7cd687
--- /dev/null
+++ b/client/src/hooks/query/paymentMades.js
@@ -0,0 +1,91 @@
+import { useMutation, useQuery, useQueryClient } from 'react-query';
+import ApiService from 'services/ApiService';
+
+const transformPaymentMades = (response) => {
+ return {};
+};
+
+/**
+ * Retrieve payment mades list.
+ */
+export function usePaymentMades(query, props) {
+ return useQuery(
+ ['PAYMENT_MADES', query],
+ () =>
+ ApiService.get('sales/payment_mades', { params: query }).then(
+ transformPaymentMades,
+ ),
+ {
+ initialData: [],
+ ...props,
+ },
+ );
+}
+
+/**
+ * Creates payment made.
+ */
+export function useCreatePaymentMade(props) {
+ const client = useQueryClient();
+
+ return useMutation(
+ (values) => ApiService.post('sales/payment_mades', values),
+ {
+ onSuccess: () => {
+ client.invalidateQueries('PAYMENT_MADES');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Edits payment made.
+ */
+export function useEditPaymentMade(props) {
+ const client = useQueryClient();
+
+ return useMutation(
+ (id, values) => ApiService.post(`sales/payment_mades/${id}`, values),
+ {
+ onSuccess: () => {
+ client.invalidateQueries('PAYMENT_MADES');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Deletes payment made.
+ */
+export function useDeletePaymentMade(props) {
+ const client = useQueryClient();
+
+ return useMutation(
+ (id, values) => ApiService.delete(`sales/payment_mades/${id}`, values),
+ {
+ onSuccess: () => {
+ client.invalidateQueries('PAYMENT_MADES');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Retrieve specific payment made.
+ */
+export function usePaymentMade(id, props) {
+ return useQuery(
+ ['PAYMENT_MADE', id],
+ () =>
+ ApiService.get(`sales/payment_mades/${id}`).then(
+ transformPaymentMades,
+ ),
+ {
+ initialData: [],
+ ...props,
+ },
+ );
+}
diff --git a/client/src/hooks/query/paymentReceives.js b/client/src/hooks/query/paymentReceives.js
new file mode 100644
index 000000000..ca9379153
--- /dev/null
+++ b/client/src/hooks/query/paymentReceives.js
@@ -0,0 +1,91 @@
+import { useMutation, useQuery, useQueryClient } from 'react-query';
+import ApiService from 'services/ApiService';
+
+const transformPaymentReceives = (response) => {
+ return {};
+};
+
+/**
+ * Retrieve accounts list.
+ */
+export function usePaymentReceives(query, props) {
+ return useQuery(
+ ['PAYMENT_RECEIVES', query],
+ () =>
+ ApiService.get('sales/payment_receives', { params: query }).then(
+ transformPaymentReceives,
+ ),
+ {
+ initialData: [],
+ ...props,
+ },
+ );
+}
+
+/**
+ * Creates payment receive.
+ */
+export function useCreatePaymentReceive(props) {
+ const client = useQueryClient();
+
+ return useMutation(
+ (values) => ApiService.post('sales/payment_receives', values),
+ {
+ onSuccess: () => {
+ client.invalidateQueries('PAYMENT_RECEIVES');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Edits payment receive.
+ */
+export function useEditPaymentReceive(props) {
+ const client = useQueryClient();
+
+ return useMutation(
+ (id, values) => ApiService.post(`sales/payment_receives/${id}`, values),
+ {
+ onSuccess: () => {
+ client.invalidateQueries('PAYMENT_RECEIVES');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Deletes payment receive.
+ */
+export function useDeletePaymentReceive(props) {
+ const client = useQueryClient();
+
+ return useMutation(
+ (id, values) => ApiService.delete(`sales/payment_receives/${id}`, values),
+ {
+ onSuccess: () => {
+ client.invalidateQueries('PAYMENT_RECEIVES');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Retrieve specific payment receive.
+ */
+export function usePaymentReceive(id, props) {
+ return useQuery(
+ ['PAYMENT_RECEIVE', id],
+ () =>
+ ApiService.get(`sales/payment_receives/${id}`).then(
+ transformPaymentReceives,
+ ),
+ {
+ initialData: [],
+ ...props,
+ },
+ );
+}
diff --git a/client/src/hooks/query/receipts.js b/client/src/hooks/query/receipts.js
new file mode 100644
index 000000000..23a244fd6
--- /dev/null
+++ b/client/src/hooks/query/receipts.js
@@ -0,0 +1,114 @@
+import { useQueryClient, useQuery, useMutation } from 'react-query';
+import ApiService from 'services/ApiService';
+
+// Receipts transformer.
+const invoicesTransformer = (response) => {
+ return {
+ invoices: response.data.sales_invoices,
+ pagination: response.data.pagination,
+ filterMeta: response.data.filter_meta,
+ };
+};
+
+const receiptTransformer = (response) => {
+ return response.data;
+}
+/**
+ * Creates a new sale invoice.
+ */
+export function useCreateReceipt(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation((values) => ApiService.post('sales/receipts', values), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SALE_RECEIPTS');
+ },
+ ...props,
+ });
+}
+
+/**
+ * Edits the given sale invoice.
+ */
+export function useEditReceipt(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ (id, values) => ApiService.post(`sales/receipts/${id}`, values),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SALE_RECEIPTS');
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Deletes the given sale invoice.
+ */
+export function useDeleteReceipt(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation((id) => ApiService.delete(`sales/receipts/${id}`), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SALE_RECEIPTS');
+ },
+ ...props,
+ });
+}
+
+/**
+ * Deletes the given sale invoice.
+ */
+export function useCloseReceipt(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation((id) => ApiService.post(`sales/receipts/${id}/close`), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SALE_RECEIPTS');
+ },
+ ...props,
+ });
+}
+
+/**
+ * Retrieve sale invoices list with pagination meta.
+ */
+export function useReceipts(query, props) {
+ return useQuery(
+ ['SALE_RECEIPTS', query],
+ () =>
+ ApiService
+ .get('sales/receipts', { params: query })
+ .then(invoicesTransformer),
+ {
+ initialData: {
+ saleReceipts: [],
+ pagination: {
+ page: 1,
+ page_size: 12,
+ total: 0,
+ },
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Retrieve sale invoices list with pagination meta.
+ */
+export function useReceipt(id, props) {
+ return useQuery(
+ ['SALE_RECEIPT', id],
+ () =>
+ ApiService
+ .get(`sales/receipts/${id}`)
+ .then(receiptTransformer),
+ {
+ initialData: {},
+ ...props,
+ },
+ );
+}
diff --git a/client/src/hooks/query/settings.js b/client/src/hooks/query/settings.js
new file mode 100644
index 000000000..64301e12d
--- /dev/null
+++ b/client/src/hooks/query/settings.js
@@ -0,0 +1,45 @@
+import { useQuery, useMutation, useQueryClient } from 'react-query';
+import { useDispatch } from 'react-redux';
+import ApiService from 'services/ApiService';
+import t from 'store/types';
+
+/**
+ * Retrieve settings.
+ */
+export function useSettings(query, props) {
+ const dispatch = useDispatch();
+
+ const settings = useQuery(
+ ['SETTINGS', query],
+ async () => {
+ const {
+ data: { settings },
+ } = await ApiService.get('settings', { params: query });
+
+ return settings;
+ },
+ {
+ initialData: [],
+ ...props,
+ },
+ );
+ dispatch({
+ type: t.SETTING_SET,
+ options: settings.data,
+ });
+ return settings;
+}
+
+/**
+ * Saves the settings.
+ */
+export function useSaveSettings(props) {
+ const queryClient = useQueryClient();
+
+ return useMutation((settings) => ApiService.post('settings', settings), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('SETTINGS');
+ },
+ ...props,
+ });
+}
diff --git a/client/src/hooks/query/vendors.js b/client/src/hooks/query/vendors.js
new file mode 100644
index 000000000..040e09605
--- /dev/null
+++ b/client/src/hooks/query/vendors.js
@@ -0,0 +1,81 @@
+
+import { useMutation, useQuery } from 'react-query';
+import ApiService from 'services/ApiService';
+
+
+const transformVendors = (response) => {
+ return {
+ vendors: response.data.vendors,
+ pagination: response.data.pagination,
+ };
+};
+
+const transformVendor = (response) => {
+ return response.data.vendor;
+};
+
+/**
+ * Retrieve vendors list.
+ */
+export function useVendors(query, props) {
+ return useQuery(
+ ['VENDORS', query],
+ () => ApiService
+ .get(`vendors`, { params: query })
+ .then(transformVendors),
+ {
+ initialData: {
+ vendors: [],
+ pagination: {},
+ },
+ ...props
+ }
+ )
+}
+
+/**
+ *
+ * @param {*} props
+ */
+export function useEditVendor(props) {
+ return useMutation(
+ (values, id) => ApiService.post(`vendors/${id}`, values),
+ props
+ );
+}
+
+/**
+ *
+ * @param {*} props
+ */
+export function useDeleteVendor(props) {
+ return useMutation(
+ (id) => ApiService.delete(`vendors/${id}`),
+ props
+ );
+}
+
+/**
+ * Creates a new vendor.
+ */
+export function useCreateVendor(props) {
+ return useMutation(
+ (values) => ApiService.post('vendors', values),
+ props
+ );
+}
+
+/**
+ *
+ * @param {*} id
+ * @param {*} props
+ */
+export function useVendor(id, props) {
+ return useQuery(
+ ['VENDOR', id],
+ () => ApiService
+ .get(`vendors/${id}`)
+ .then(transformVendor),
+ props
+ )
+};
\ No newline at end of file
diff --git a/client/src/hooks/query/views.js b/client/src/hooks/query/views.js
new file mode 100644
index 000000000..e88d9b3d0
--- /dev/null
+++ b/client/src/hooks/query/views.js
@@ -0,0 +1,51 @@
+import { useQuery } from 'react-query';
+import ApiService from "services/ApiService";
+
+// export function useSaveView(values) {
+// return ApiService.post('views', form);
+// }
+
+// export function useEditView(values, id) {
+// return ApiService.post(`views/${id}`, values);
+// }
+
+// export function useDeleteView(id) {
+// return ApiService.delete(`views/${id}`);
+// }
+
+// export function useView(id) {
+// return useQuery(['VIEW', id], () => ApiService.get(`views/${id}`)
+// }
+
+export function useResourceViews(resourceSlug) {
+ return useQuery(
+ ['RESOURCE_VIEW', resourceSlug],
+ () => ApiService.get(`views/resource/${resourceSlug}`)
+ .then((response) => response.data.views),
+ {
+ initialData: [],
+ }
+ );
+}
+
+
+export function useResourceColumns(resourceSlug) {
+ return useQuery(
+ ['RESOURCE_COLUMNS', resourceSlug],
+ () => ApiService.get(`resources/${resourceSlug}/columns`),
+ {
+ initialData: [],
+ },
+ );
+}
+
+export function useResourceFields(resourceSlug) {
+ return useQuery(
+ ['RESOURCE_FIELDS', resourceSlug],
+ () => ApiService.get(`resources/${resourceSlug}/fields`)
+ .then((res) => res.data.resource_fields),
+ {
+ initialData: [],
+ },
+ );
+}
diff --git a/client/src/hooks/state/dashboard.js b/client/src/hooks/state/dashboard.js
new file mode 100644
index 000000000..75a85f3f4
--- /dev/null
+++ b/client/src/hooks/state/dashboard.js
@@ -0,0 +1,10 @@
+import { useDispatch } from 'react-redux';
+import { dashboardPageTitle } from 'store/dashboard/dashboard.actions';
+
+export const useDashboardPageTitle = () => {
+ const dispatch = useDispatch();
+
+ return (pageTitle) => {
+ dispatch(dashboardPageTitle(pageTitle));
+ }
+};
diff --git a/client/src/hooks/state/index.js b/client/src/hooks/state/index.js
new file mode 100644
index 000000000..b69182297
--- /dev/null
+++ b/client/src/hooks/state/index.js
@@ -0,0 +1 @@
+export * from './dashboard';
\ No newline at end of file
diff --git a/client/src/index.js b/client/src/index.js
index f4d1ecca0..ea6c3bf30 100644
--- a/client/src/index.js
+++ b/client/src/index.js
@@ -7,7 +7,6 @@ import 'services/yup';
import App from 'components/App';
import * as serviceWorker from 'serviceWorker';
import { store, persistor } from 'store/createStore';
-import AppProgress from 'components/NProgress/AppProgress';
if (process.env.NODE_ENV === 'development') {
const whyDidYouRender = require('@welldone-software/why-did-you-render');
@@ -28,13 +27,6 @@ ReactDOM.render(
document.getElementById('root'),
);
-ReactDOM.render(
-
-
- ,
- document.getElementById('nprogress'),
-);
-
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
diff --git a/client/src/lang/en/index.js b/client/src/lang/en/index.js
index d31656aa4..c58470627 100644
--- a/client/src/lang/en/index.js
+++ b/client/src/lang/en/index.js
@@ -484,7 +484,7 @@ export default {
open_sidebar: 'Open sidebar.',
recalc_report: 'Re-calc Report',
remove_the_line: 'Remove the line',
- no_results: 'No results',
+ no_results: 'No results.',
all_reports: 'All Reports',
next: 'Next',
previous: 'Previous',
@@ -923,7 +923,6 @@ export default {
opening_average_cost: 'Opening average cost',
opening_cost_: 'Opening cost ',
opening_date_: 'Opening date ',
- no_results: 'No results.',
the_invoice_cannot_be_deleted:
'The invoice cannot be deleted cause has associated payment transactions',
category_name_exists: 'Category name exists',
diff --git a/client/src/routes/dashboard.js b/client/src/routes/dashboard.js
index 15c40a3fb..b6d8465c0 100644
--- a/client/src/routes/dashboard.js
+++ b/client/src/routes/dashboard.js
@@ -62,7 +62,7 @@ export default [
{
path: `/items/categories`,
component: LazyLoader({
- loader: () => import('containers/Items/ItemCategoriesList'),
+ loader: () => import('containers/ItemsCategories/ItemCategoriesList'),
}),
breadcrumb: 'Categories',
},
@@ -95,7 +95,7 @@ export default [
{
path: `/inventory-adjustments`,
component: LazyLoader({
- loader: () => import('containers/Items/InventoryAdjustmentList'),
+ loader: () => import('containers/InventoryAdjustments/InventoryAdjustmentList'),
}),
breadcrumb: 'Inventory a adjustments',
},
diff --git a/client/src/store/accounts/accounts.actions.js b/client/src/store/accounts/accounts.actions.js
index 6f7568608..256804c6e 100644
--- a/client/src/store/accounts/accounts.actions.js
+++ b/client/src/store/accounts/accounts.actions.js
@@ -75,12 +75,16 @@ export const fetchAccountsTable = ({ query } = {}) => {
type: t.ACCOUNTS_ITEMS_SET,
accounts: response.data.accounts,
});
+ dispatch({
+ type: t.ACCOUNTS_SET_CURRENT_VIEW,
+ currentViewId: parseInt(response.data?.filter_meta?.view?.custom_view_id, 10),
+ });
dispatch({
type: t.ACCOUNTS_TABLE_LOADING,
loading: false,
});
+ resolve(response);
});
- resolve(response);
})
.catch((error) => {
reject(error);
diff --git a/client/src/store/accounts/accounts.reducer.js b/client/src/store/accounts/accounts.reducer.js
index 3c304cc89..1630c175c 100644
--- a/client/src/store/accounts/accounts.reducer.js
+++ b/client/src/store/accounts/accounts.reducer.js
@@ -1,7 +1,9 @@
import t from 'store/types';
import { createReducer} from '@reduxjs/toolkit';
-import { createTableQueryReducers } from 'store/queryReducers';
import { listToTree } from 'utils';
+import {
+ createTableQueryReducers,
+} from 'store/journalNumber.reducer';
const initialState = {
items: {},
@@ -20,7 +22,7 @@ const initialState = {
errors: [],
};
-const accountsReducer = createReducer(initialState, {
+export default createReducer(initialState, {
[t.ACCOUNTS_ITEMS_SET]: (state, action) => {
const _items = {};
@@ -107,10 +109,10 @@ const accountsReducer = createReducer(initialState, {
[t.ACCOUNTS_SELECTED_ROWS_SET]: (state, action) => {
const { selectedRows } = action.payload;
state.selectedRows = selectedRows;
- }
-});
+ },
-export default createTableQueryReducers('accounts', accountsReducer);
+ ...createTableQueryReducers('ACCOUNTS'),
+});
export const getAccountById = (state, id) => {
return state.accounts.accountsById[id];
diff --git a/client/src/store/accounts/accounts.types.js b/client/src/store/accounts/accounts.types.js
index 283f6d194..9056415dc 100644
--- a/client/src/store/accounts/accounts.types.js
+++ b/client/src/store/accounts/accounts.types.js
@@ -20,7 +20,6 @@ export default {
ACCOUNTS_TABLE_QUERIES_SET: 'ACCOUNTS/TABLE_QUERIES_SET',
ACCOUNTS_TABLE_QUERIES_ADD:'ACCOUNTS/TABLE_QUERIES_ADD',
-
ACCOUNTS_TABLE_LOADING: 'ACCOUNTS_TABLE_LOADING',
ACCOUNT_ERRORS_SET: 'ACCOUNT_ERRORS_SET',
diff --git a/client/src/store/dashboard/dashboard.actions.js b/client/src/store/dashboard/dashboard.actions.js
index 2f186efac..5403dfaa3 100644
--- a/client/src/store/dashboard/dashboard.actions.js
+++ b/client/src/store/dashboard/dashboard.actions.js
@@ -1,5 +1,12 @@
import t from 'store/types';
+export function dashboardPageTitle(pageTitle) {
+ return {
+ type: t.CHANGE_DASHBOARD_PAGE_TITLE,
+ pageTitle,
+ };
+}
+
export function openDialog(name, payload) {
return {
type: t.OPEN_DIALOG,
diff --git a/client/src/store/financialStatement/financialStatements.mappers.js b/client/src/store/financialStatement/financialStatements.mappers.js
index bc21867f5..e771198cf 100644
--- a/client/src/store/financialStatement/financialStatements.mappers.js
+++ b/client/src/store/financialStatement/financialStatements.mappers.js
@@ -24,6 +24,8 @@ export const mapBalanceSheetToTableRows = (accounts) => {
});
};
+export const profitLossToTableRowsMapper = () => {};
+
export const journalToTableRowsMapper = (journal) => {
const TYPES = {
ENTRY: 'ENTRY',
@@ -151,113 +153,3 @@ export const mapTrialBalanceSheetToRows = (sheet) => {
}
return results;
};
-
-export const profitLossToTableRowsMapper = (profitLoss) => {
- const results = [];
-
- if (profitLoss.income) {
- results.push({
- name: 'Income',
- total: profitLoss.income.total,
- children: [
- ...profitLoss.income.accounts,
- {
- name: 'Total Income',
- total: profitLoss.income.total,
- total_periods: profitLoss.income.total_periods,
- rowTypes: ['income_total', 'section_total', 'total'],
- },
- ],
- total_periods: profitLoss.income.total_periods,
- });
- }
- if (profitLoss.cost_of_sales) {
- results.push({
- name: 'Cost of sales',
- total: profitLoss.cost_of_sales.total,
- children: [
- ...profitLoss.cost_of_sales.accounts,
- {
- name: 'Total cost of sales',
- total: profitLoss.cost_of_sales.total,
- total_periods: profitLoss.cost_of_sales.total_periods,
- rowTypes: ['cogs_total', 'section_total', 'total'],
- },
- ],
- total_periods: profitLoss.cost_of_sales.total_periods,
- });
- }
- if (profitLoss.gross_profit) {
- results.push({
- name: 'Gross profit',
- total: profitLoss.gross_profit.total,
- total_periods: profitLoss.gross_profit.total_periods,
- rowTypes: ['gross_total', 'section_total', 'total'],
- })
- }
- if (profitLoss.expenses) {
- results.push({
- name: 'Expenses',
- total: profitLoss.expenses.total,
- children: [
- ...profitLoss.expenses.accounts,
- {
- name: 'Total Expenses',
- total: profitLoss.expenses.total,
- total_periods: profitLoss.expenses.total_periods,
- rowTypes: ['expenses_total', 'section_total', 'total'],
- },
- ],
- total_periods: profitLoss.expenses.total_periods,
- })
- }
- if (profitLoss.operating_profit) {
- results.push({
- name: 'Net Operating income',
- total: profitLoss.operating_profit.total,
- total_periods: profitLoss.income.total_periods,
- rowTypes: ['net_operating_total', 'section_total', 'total'],
- })
- }
- if (profitLoss.other_income) {
- results.push({
- name: 'Other Income',
- total: profitLoss.other_income.total,
- total_periods: profitLoss.other_income.total_periods,
- children: [
- ...profitLoss.other_income.accounts,
- {
- name: 'Total other income',
- total: profitLoss.other_income.total,
- total_periods: profitLoss.other_income.total_periods,
- rowTypes: ['expenses_total', 'section_total', 'total'],
- },
- ],
- });
- }
- if (profitLoss.other_expenses) {
- results.push({
- name: 'Other expenses',
- total: profitLoss.other_expenses.total,
- total_periods: profitLoss.other_expenses.total_periods,
- children: [
- ...profitLoss.other_expenses.accounts,
- {
- name: 'Total other expenses',
- total: profitLoss.other_expenses.total,
- total_periods: profitLoss.other_expenses.total_periods,
- rowTypes: ['expenses_total', 'section_total', 'total'],
- },
- ],
- });
- }
- if (profitLoss.net_income) {
- results.push({
- name: 'Net Income',
- total: profitLoss.net_income.total,
- total_periods: profitLoss.net_income.total_periods,
- rowTypes: ['net_income_total', 'section_total', 'total'],
- })
- };
- return results;
-};
diff --git a/client/src/store/queryReducers.js b/client/src/store/queryReducers.js
index 9225eab3b..738f7bd1d 100644
--- a/client/src/store/queryReducers.js
+++ b/client/src/store/queryReducers.js
@@ -16,7 +16,11 @@ export const createTableQueryReducers =
};
case `${RESOURCE_NAME}/TABLE_QUERIES_ADD`:
return {
-
+ ...state,
+ tableQuery: {
+ ...state.tableQuery,
+ ...action.payload.query,
+ }
};
default:
return reducer(state, action);
diff --git a/client/src/style/components/DataTable/DataTable.scss b/client/src/style/components/DataTable/DataTable.scss
index d5796d9ca..012106167 100644
--- a/client/src/style/components/DataTable/DataTable.scss
+++ b/client/src/style/components/DataTable/DataTable.scss
@@ -131,6 +131,23 @@
border-color: #0052ff;
}
}
+
+ .skeleton{
+ animation: skeleton-fade-in .3s linear forwards,
+ skeleton-glow 1s linear infinite alternate;
+
+ animation-delay: 0s,.3s;
+ height: 8px;
+ opacity: 1;
+ width: 65%;
+ margin: 10px 0;
+ }
+ }
+
+ .th{
+ .skeleton{
+ margin: 5px 0;
+ }
}
.tbody {
diff --git a/client/src/style/components/PageForm.scss b/client/src/style/components/PageForm.scss
index 435c686f2..e6b005395 100644
--- a/client/src/style/components/PageForm.scss
+++ b/client/src/style/components/PageForm.scss
@@ -14,6 +14,21 @@
padding: 14px 18px;
border-top: 1px solid rgba(0, 0, 0, 0.05);
box-shadow: 0px -1px 4px 0px rgba(0, 0, 0, 0.05);
+
+ .bp3-button-group{
+
+ .bp3-button{
+ &:not(:last-child),
+ &.bp3-popover-wrapper:not(:last-child) {
+ border-right: 1px solid rgba(92, 112, 127, 0.3);
+ margin-right: 0;
+
+ &.bp3-intent-primary{
+ border-right: 1px solid rgba(255, 255, 255, 0.3);
+ }
+ }
+ }
+ }
}
&--strip {
#{$self}__header-fields {
diff --git a/client/src/style/components/Skeleton.scss b/client/src/style/components/Skeleton.scss
new file mode 100644
index 000000000..bc05ef965
--- /dev/null
+++ b/client/src/style/components/Skeleton.scss
@@ -0,0 +1,26 @@
+
+
+@keyframes skeleton-glow {
+ 0% {
+ background: rgba(206,217,224,.3);
+ border-color: rgba(206,217,224,.3);
+ }
+ to {
+ background: rgba(92,112,128,.3);
+ border-color: rgba(92,112,128,.3);
+ }
+}
+
+.skeleton{
+ animation: skeleton-glow 1s linear infinite alternate;
+ background: rgba(206,217,224,.3);
+ background-clip: padding-box;
+ border-color: rgba(206,217,224,.3);
+ border-radius: 2px;
+
+ box-shadow: none;
+ color: transparent;
+ cursor: default;
+ pointer-events: none;
+ user-select: none;
+}
\ No newline at end of file
diff --git a/client/src/style/containers/Dashboard/Sidebar.scss b/client/src/style/containers/Dashboard/Sidebar.scss
index 3d6fb2a28..a748f728e 100644
--- a/client/src/style/containers/Dashboard/Sidebar.scss
+++ b/client/src/style/containers/Dashboard/Sidebar.scss
@@ -29,7 +29,7 @@
}
}
&__head {
- padding: 18px;
+ padding: 18px 20px;
&-logo {
position: relative;
@@ -99,7 +99,7 @@
.#{$ns}-submenu {
.#{$ns}-collapse {
&-body {
- background-color: rgba(255, 255, 255, 0.02);
+ background-color: rgba(255, 255, 255, 0.03);
padding-bottom: 6px;
padding-top: 6px;
}
@@ -147,7 +147,7 @@
.sidebar__head{
.sidebar__head-logo{
- transform: translate(-6px, 0);
+ transform: translate(-8px, 0);
}
}
// Hide text of bigcapital logo.
diff --git a/client/src/style/pages/Dashboard/Dashboard.scss b/client/src/style/pages/Dashboard/Dashboard.scss
index 4078923d8..e0db1af11 100644
--- a/client/src/style/pages/Dashboard/Dashboard.scss
+++ b/client/src/style/pages/Dashboard/Dashboard.scss
@@ -227,8 +227,8 @@ $dashboard-views-bar-height: 45px;
border-left: 1px solid #d9d9d9;
padding-left: 10px;
font-size: 16px;
- font-weight: 300;
- color: #101052;
+ font-weight: 400;
+ color: #76768b;
margin: 0 0 0 12px;
padding-top: 4px;
padding-bottom: 4px;
diff --git a/client/src/style/pages/FinancialStatements/ProfitLossSheet.scss b/client/src/style/pages/FinancialStatements/ProfitLossSheet.scss
index 97e3e4bad..a90bbe12a 100644
--- a/client/src/style/pages/FinancialStatements/ProfitLossSheet.scss
+++ b/client/src/style/pages/FinancialStatements/ProfitLossSheet.scss
@@ -6,7 +6,7 @@
.tbody{
.tr .td:not(:first-child),
.tr .th:not(:first-child) {
- justify-content: flex-end;
+ text-align: right;
}
}
.tbody{
diff --git a/client/src/style/pages/ItemCategory/ItemCategoryDialog.scss b/client/src/style/pages/ItemCategory/ItemCategoryDialog.scss
index 962e7be24..2ef5363b3 100644
--- a/client/src/style/pages/ItemCategory/ItemCategoryDialog.scss
+++ b/client/src/style/pages/ItemCategory/ItemCategoryDialog.scss
@@ -27,4 +27,11 @@
}
}
}
+ .bp3-dialog-footer{
+ margin-top: 30px;
+
+ .bp3-button{
+ min-width: 75px;
+ }
+ }
}
\ No newline at end of file
diff --git a/client/src/style/pages/Items/PageForm.scss b/client/src/style/pages/Items/PageForm.scss
index db114a7e0..9602fac53 100644
--- a/client/src/style/pages/Items/PageForm.scss
+++ b/client/src/style/pages/Items/PageForm.scss
@@ -72,6 +72,10 @@
margin: 0;
margin-left: 40px;
}
+
+ .btn--submit{
+ min-width: 65px;
+ }
}
.bp3-tooltip-indicator {
diff --git a/client/src/utils.js b/client/src/utils.js
index b9df865c6..25c671015 100644
--- a/client/src/utils.js
+++ b/client/src/utils.js
@@ -404,7 +404,7 @@ export const toSafeNumber = (number) => {
};
export const transformToCamelCase = (object) => {
- return deepMapKeys(object, (key) => _.snakeCase(key));
+ return deepMapKeys(object, (key) => _.camelCase(key));
};
export const transfromToSnakeCase = (object) => {
@@ -432,3 +432,38 @@ export function flatObject(obj) {
return flatObject;
}
+
+export function randomNumber(min, max) {
+ if (min > max) {
+ let temp = max;
+ max = min;
+ min = temp;
+ }
+ if (min <= 0) {
+ return Math.floor(Math.random() * (max + Math.abs(min) + 1)) + min;
+ } else {
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+ }
+}
+
+
+export function transformResponse(response) {
+ return transformToCamelCase(response);
+}
+
+export function transactionNumber(prefix, number) {
+ const codes = [];
+
+ if (prefix) {
+ codes.push(prefix);
+ }
+ if (number) {
+ codes.push(number);
+ }
+ return codes.join('-');
+
+}
+
+export function safeCallback(callback, ...args) {
+ return () => callback && callback(...args);
+}
\ No newline at end of file
diff --git a/server/src/data/AccountTypes.ts b/server/src/data/AccountTypes.ts
index 649db460b..f3b108627 100644
--- a/server/src/data/AccountTypes.ts
+++ b/server/src/data/AccountTypes.ts
@@ -1,5 +1,3 @@
-
-
export const ACCOUNT_TYPE = {
CASH: 'cash',
BANK: 'bank',
@@ -42,11 +40,10 @@ export const ACCOUNT_ROOT_TYPE = {
ASSET: 'asset',
LIABILITY: 'liability',
EQUITY: 'equity',
- EXPENSE: 'expene',
+ EXPENSE: 'expense',
INCOME: 'income',
};
-
export const ACCOUNT_NORMAL = {
CREDIT: 'credit',
DEBIT: 'debit',
@@ -223,4 +220,4 @@ export const ACCOUNT_TYPES = [
balanceSheet: false,
incomeSheet: true,
},
-];
\ No newline at end of file
+];
diff --git a/server/src/database/seeds/core/20200810121808_seed_views_roles.js b/server/src/database/seeds/core/20200810121808_seed_views_roles.js
index b4c1ba5c7..674a823d1 100644
--- a/server/src/database/seeds/core/20200810121808_seed_views_roles.js
+++ b/server/src/database/seeds/core/20200810121808_seed_views_roles.js
@@ -6,11 +6,11 @@ exports.up = (knex) => {
// Inserts seed entries
return knex('view_roles').insert([
// Accounts
- { field_key: 'type', index: 1, comparator: 'equals', value: 'asset', view_id: 1 },
- { field_key: 'type', index: 1, comparator: 'equals', value: 'liability', view_id: 2 },
- { field_key: 'type', index: 1, comparator: 'equals', value: 'equity', view_id: 3 },
- { field_key: 'type', index: 1, comparator: 'equals', value: 'income', view_id: 4 },
- { field_key: 'type', index: 1, comparator: 'equals', value: 'expense', view_id: 5 },
+ { field_key: 'root_type', index: 1, comparator: 'equals', value: 'asset', view_id: 1 },
+ { field_key: 'root_type', index: 1, comparator: 'equals', value: 'liability', view_id: 2 },
+ { field_key: 'root_type', index: 1, comparator: 'equals', value: 'equity', view_id: 3 },
+ { field_key: 'root_type', index: 1, comparator: 'equals', value: 'income', view_id: 4 },
+ { field_key: 'root_type', index: 1, comparator: 'equals', value: 'expense', view_id: 5 },
{ field_key: 'active', index: 1, comparator: 'is', value: 1, view_id: 15 },
// Items.
diff --git a/server/src/models/Account.js b/server/src/models/Account.js
index f181ea162..0ea21e42c 100644
--- a/server/src/models/Account.js
+++ b/server/src/models/Account.js
@@ -32,6 +32,7 @@ export default class Account extends TenantModel {
return [
'accountTypeLabel',
'accountParentType',
+ 'accountRootType',
'accountNormal',
'isBalanceSheetAccount',
'isPLSheet'
@@ -59,6 +60,13 @@ export default class Account extends TenantModel {
return AccountTypesUtils.getType(this.accountType, 'parentType');
}
+ /**
+ * Retrieve account root type.
+ */
+ get accountRootType() {
+ return AccountTypesUtils.getType(this.accountType, 'rootType');
+ }
+
/**
* Retrieve whether the account is balance sheet account.
*/
@@ -236,9 +244,6 @@ export default class Account extends TenantModel {
},
root_type: {
label: 'Root type',
- column: 'account_type_id',
- relation: 'account_types.id',
- relationColumn: 'account_types.root_type',
options: [
{ key: 'asset', label: 'Asset', },
{ key: 'liability', label: 'Liability' },
@@ -246,7 +251,12 @@ export default class Account extends TenantModel {
{ key: 'Income', label: 'Income' },
{ key: 'expense', label: 'Expense' },
],
- fieldType: 'options',
+ query: (query, role) => {
+ const accountsTypes = AccountTypesUtils.getTypesByRootType(role.value);
+ const accountsTypesKeys = accountsTypes.map(type => type.key);
+
+ query.whereIn('account_type', accountsTypesKeys);
+ },
},
created_at: {
label: 'Created at',
diff --git a/server/src/services/Expenses/ExpensesService.ts b/server/src/services/Expenses/ExpensesService.ts
index ba5ac0324..0d4342395 100644
--- a/server/src/services/Expenses/ExpensesService.ts
+++ b/server/src/services/Expenses/ExpensesService.ts
@@ -157,7 +157,7 @@ export default class ExpensesService implements IExpensesService {
const invalidExpenseAccounts: number[] = [];
expensesAccounts.forEach((expenseAccount) => {
- if (expenseAccount.isRootType(ACCOUNT_ROOT_TYPE.EXPENSE)) {
+ if (!expenseAccount.isRootType(ACCOUNT_ROOT_TYPE.EXPENSE)) {
invalidExpenseAccounts.push(expenseAccount.id);
}
});
@@ -181,7 +181,7 @@ export default class ExpensesService implements IExpensesService {
paymentAccount,
});
- if (paymentAccount.isParentType(ACCOUNT_PARENT_TYPE.CURRENT_ASSET)) {
+ if (!paymentAccount.isParentType(ACCOUNT_PARENT_TYPE.CURRENT_ASSET)) {
this.logger.info(
'[expenses] the given payment account has invalid type',
{ tenantId, paymentAccount }