diff --git a/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummary.js b/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummary.js
new file mode 100644
index 000000000..05b35d20e
--- /dev/null
+++ b/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummary.js
@@ -0,0 +1,87 @@
+import React, { useEffect, useState } from 'react';
+import moment from 'moment';
+
+import 'style/pages/FinancialStatements/ContactsBalanceSummary.scss';
+
+import { FinancialStatement } from 'components';
+import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
+
+import VendorsBalanceSummaryActionsBar from './VendorsBalanceSummaryActionsBar';
+import VendorsBalanceSummaryHeader from './VendorsBalanceSummaryHeader';
+import VendorsBalanceSummaryTable from './VendorsBalanceSummaryTable';
+
+import { VendorsBalanceSummaryProvider } from './VendorsBalanceSummaryProvider';
+import { VendorsSummarySheetLoadingBar } from './components';
+import withVendorsBalanceSummaryActions from './withVendorsBalanceSummaryActions';
+
+import withSettings from 'containers/Settings/withSettings';
+
+import { compose } from 'utils';
+
+/**
+ * Vendors Balance summary.
+ */
+function VendorsBalanceSummary({
+ // #withPreferences
+ organizationName,
+
+ // #withVendorsBalanceSummaryActions
+ toggleVendorSummaryFilterDrawer,
+}) {
+ const [filter, setFilter] = useState({
+ asDate: moment().endOf('day').format('YYYY-MM-DD'),
+ });
+
+ // Handle refetch vendors balance summary.
+ const handleFilterSubmit = (filter) => {
+ const _filter = {
+ ...filter,
+ asDate: moment(filter.asDate).format('YYYY-MM-DD'),
+ };
+ setFilter(_filter);
+ };
+
+ // Handle number format submit.
+ const handleNumberFormatSubmit = (format) => {
+ setFilter({
+ ...filter,
+ numberFormat: format,
+ });
+ };
+
+ useEffect(
+ () => () => {
+ toggleVendorSummaryFilterDrawer(false);
+ },
+ [toggleVendorSummaryFilterDrawer],
+ );
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default compose(
+ withSettings(({ organizationSettings }) => ({
+ organizationName: organizationSettings?.name,
+ })),
+ withVendorsBalanceSummaryActions,
+)(VendorsBalanceSummary);
diff --git a/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryActionsBar.js b/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryActionsBar.js
new file mode 100644
index 000000000..f7f01655c
--- /dev/null
+++ b/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryActionsBar.js
@@ -0,0 +1,126 @@
+import React from 'react';
+import {
+ NavbarDivider,
+ NavbarGroup,
+ Classes,
+ Button,
+ Popover,
+ PopoverInteractionKind,
+ Position,
+} from '@blueprintjs/core';
+import { FormattedMessage as T } from 'react-intl';
+import classNames from 'classnames';
+
+import { Icon } from 'components';
+import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
+import NumberFormatDropdown from 'components/NumberFormatDropdown';
+
+import withVendorsBalanceSummary from './withVendorsBalanceSummary';
+import withVendorsBalanceSummaryActions from './withVendorsBalanceSummaryActions';
+import { useVendorsBalanceSummaryContext } from './VendorsBalanceSummaryProvider';
+
+import { saveInvoke, compose } from 'utils';
+
+/**
+ * Vendors balance summary action bar.
+ */
+function VendorsBalanceSummaryActionsBar({
+ //#ownProps
+ numberFormat,
+ onNumberFormatSubmit,
+
+ // #withVendorsBalanceSummary
+ isFilterDrawerOpen,
+
+ // #withVendorsBalanceSummaryActions
+ toggleVendorSummaryFilterDrawer,
+}) {
+ const {
+ isVendorsBalanceLoading,
+ refetch,
+ } = useVendorsBalanceSummaryContext();
+
+ const handleFilterToggleClick = () => {
+ toggleVendorSummaryFilterDrawer();
+ };
+
+ // handle recalculate report button.
+ const handleRecalculateReport = () => {
+ refetch();
+ };
+
+ // handle number format submit.
+ const handleNumberFormatSubmit = (numberFormat) => {
+ saveInvoke(onNumberFormatSubmit, numberFormat);
+ };
+
+ return (
+
+
+ }
+ onClick={handleRecalculateReport}
+ icon={}
+ />
+
+ }
+ text={
+ isFilterDrawerOpen ? (
+
+ ) : (
+
+ )
+ }
+ onClick={handleFilterToggleClick}
+ active={isFilterDrawerOpen}
+ />
+
+
+ }
+ minimal={true}
+ interactionKind={PopoverInteractionKind.CLICK}
+ position={Position.BOTTOM_LEFT}
+ >
+ }
+ icon={}
+ />
+
+
+ }
+ icon={}
+ />
+
+
+ }
+ text={}
+ />
+ }
+ text={}
+ />
+
+
+ );
+}
+export default compose(
+ withVendorsBalanceSummaryActions,
+ withVendorsBalanceSummary(({ VendorsSummaryFilterDrawer }) => ({
+ isFilterDrawerOpen: VendorsSummaryFilterDrawer,
+ })),
+)(VendorsBalanceSummaryActionsBar);
diff --git a/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryHeader.js b/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryHeader.js
new file mode 100644
index 000000000..fb5be3c6f
--- /dev/null
+++ b/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryHeader.js
@@ -0,0 +1,89 @@
+import React from 'react';
+import * as Yup from 'yup';
+import { Formik, Form } from 'formik';
+import moment from 'moment';
+import { Tabs, Tab, Button, Intent } from '@blueprintjs/core';
+import { FormattedMessage as T } from 'react-intl';
+
+import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
+import withVendorsBalanceSummary from './withVendorsBalanceSummary';
+import withVendorsBalanceSummaryActions from './withVendorsBalanceSummaryActions';
+import VendorsBalanceSummaryHeaderGeneral from './VendorsBalanceSummaryHeaderGeneral';
+
+import { compose } from 'utils';
+
+/**
+ * Vendors balance summary drawer header.
+ */
+function VendorsBalanceSummaryHeader({
+ // #ownProps
+ pageFilter,
+ onSubmitFilter,
+
+ //#withVendorsBalanceSummary
+ VendorsSummaryFilterDrawer,
+
+ //#withVendorsBalanceSummaryActions
+ toggleVendorSummaryFilterDrawer,
+}) {
+ // validation schema.
+ const validationSchema = Yup.object().shape({
+ asDate: Yup.date().required().label('asDate'),
+ });
+
+ // filter form initial values.
+ const initialValues = {
+ ...pageFilter,
+ asDate: moment(pageFilter.asDate).toDate(),
+ };
+
+ // handle form submit.
+ const handleSubmit = (values, { setSubmitting }) => {
+ onSubmitFilter(values);
+ toggleVendorSummaryFilterDrawer(false);
+ setSubmitting(false);
+ };
+
+ // handle cancel button click.
+ const handleCancelClick = () => {
+ toggleVendorSummaryFilterDrawer(false);
+ };
+
+ return (
+
+
+
+
+
+ );
+}
+
+export default compose(
+ withVendorsBalanceSummary(({ VendorsSummaryFilterDrawer }) => ({
+ VendorsSummaryFilterDrawer,
+ })),
+ withVendorsBalanceSummaryActions,
+)(VendorsBalanceSummaryHeader);
diff --git a/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryHeaderGeneral.js b/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryHeaderGeneral.js
new file mode 100644
index 000000000..b7811721a
--- /dev/null
+++ b/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryHeaderGeneral.js
@@ -0,0 +1,64 @@
+import React from 'react';
+import { FastField } from 'formik';
+import { DateInput } from '@blueprintjs/datetime';
+import { FormGroup, Position, Classes, Checkbox } from '@blueprintjs/core';
+import { FormattedMessage as T } from 'react-intl';
+import { Row, Col, FieldHint } from 'components';
+import {
+ momentFormatter,
+ tansformDateValue,
+ inputIntent,
+ handleDateChange,
+} from 'utils';
+
+/**
+ * Vendors balance header -general panel.
+ */
+export default function VendorsBalanceSummaryHeaderGeneral() {
+ return (
+
+
+
+
+ {({ form, field: { value }, meta: { error } }) => (
+ }
+ labelInfo={}
+ fill={true}
+ intent={inputIntent({ error })}
+ >
+ {
+ form.setFieldValue('asDate', selectedDate);
+ })}
+ popoverProps={{ position: Position.BOTTOM, minimal: true }}
+ minimal={true}
+ fill={true}
+ />
+
+ )}
+
+
+
+
+
+
+ {({ field }) => (
+ }>
+
+
+ )}
+
+
+
+
+ );
+}
diff --git a/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryProvider.js b/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryProvider.js
new file mode 100644
index 000000000..b6f5587fb
--- /dev/null
+++ b/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryProvider.js
@@ -0,0 +1,41 @@
+import React from 'react';
+import FinancialReportPage from '../FinancialReportPage';
+import { useVendorsBalanceSummaryReport } from 'hooks/query';
+import { transformFilterFormToQuery } from '../common';
+
+const VendorsBalanceSummaryContext = React.createContext();
+
+/**
+ * Vendors balance summary provider.
+ */
+function VendorsBalanceSummaryProvider({ filter, ...props }) {
+ // const query = React.useMemo(() => transformFilterFormToQuery(filter), [
+ // filter,
+ // ]);
+
+ const {
+ data: VendorBalanceSummary,
+ isLoading: isVendorsBalanceLoading,
+ isFetching: isVendorsBalanceFetching,
+ refetch,
+ } = useVendorsBalanceSummaryReport(filter, {
+ keepPreviousData: true,
+ });
+
+ const provider = {
+ VendorBalanceSummary,
+ isVendorsBalanceLoading,
+ isVendorsBalanceFetching,
+ refetch,
+ };
+
+ return (
+
+
+
+ );
+}
+
+const useVendorsBalanceSummaryContext = () =>
+ React.useContext(VendorsBalanceSummaryContext);
+export { VendorsBalanceSummaryProvider, useVendorsBalanceSummaryContext };
diff --git a/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryTable.js b/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryTable.js
new file mode 100644
index 000000000..043bd2c54
--- /dev/null
+++ b/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummaryTable.js
@@ -0,0 +1,47 @@
+import React from 'react';
+import { FormattedMessage as T, useIntl } from 'react-intl';
+import { DataTable } from 'components';
+import FinancialSheet from 'components/FinancialSheet';
+
+import { useVendorsBalanceColumns } from './components';
+import { useVendorsBalanceSummaryContext } from './VendorsBalanceSummaryProvider';
+
+/**
+ * Vendors balance summary table.
+ */
+export default function VendorsBalanceSummaryTable({
+ //#ownProps
+ organizationName,
+}) {
+ const { formatMessage } = useIntl();
+
+ const {
+ VendorBalanceSummary,
+ isVendorsBalanceLoading,
+ } = useVendorsBalanceSummaryContext();
+
+ // vendors balance summary columns.
+ const columns = useVendorsBalanceColumns();
+
+ const rowClassNames = (row) => {
+ return [`row-type--${row.original.rowTypes}`];
+ };
+
+ return (
+
+
+
+ );
+}
diff --git a/client/src/containers/FinancialStatements/VendorsBalanceSummary/components.js b/client/src/containers/FinancialStatements/VendorsBalanceSummary/components.js
new file mode 100644
index 000000000..f6d9f5315
--- /dev/null
+++ b/client/src/containers/FinancialStatements/VendorsBalanceSummary/components.js
@@ -0,0 +1,47 @@
+import React, { useMemo } from 'react';
+import { formatMessage } from 'services/intl';
+
+import { If } from 'components';
+import { getColumnWidth } from 'utils';
+import FinancialLoadingBar from '../FinancialLoadingBar';
+import { useVendorsBalanceSummaryContext } from './VendorsBalanceSummaryProvider';
+
+/**
+ * Retrieve vendors balance summary columns.
+ */
+export const useVendorsBalanceColumns = () => {
+ return useMemo(() => [
+ {
+ Header: formatMessage({ id: 'vendor_name' }),
+ accessor: 'cells[0].value',
+ className: 'customer_name',
+ width: 240,
+ sticky: 'left',
+ textOverview: true,
+ },
+ {
+ Header: formatMessage({ id: 'total' }),
+ accessor: 'cells[1].value',
+ className: 'total',
+ width: 140,
+ },
+ {
+ Header: formatMessage({ id: 'percentage_of_column' }),
+ accessor: 'cells[2].value',
+ // className: 'total',
+ width: 140,
+ },
+ ]);
+};
+
+/**
+ * vendors balance summary loading bar.
+ */
+export function VendorsSummarySheetLoadingBar() {
+ const { isVendorsBalanceFetching } = useVendorsBalanceSummaryContext();
+ return (
+
+
+
+ );
+}
diff --git a/client/src/containers/FinancialStatements/VendorsBalanceSummary/withVendorsBalanceSummary.js b/client/src/containers/FinancialStatements/VendorsBalanceSummary/withVendorsBalanceSummary.js
new file mode 100644
index 000000000..674e01716
--- /dev/null
+++ b/client/src/containers/FinancialStatements/VendorsBalanceSummary/withVendorsBalanceSummary.js
@@ -0,0 +1,15 @@
+import { connect } from 'react-redux';
+import { getVendorsBalanceSummaryFilterDrawer } from 'store/financialStatement/financialStatements.selectors';
+
+export default (mapState) => {
+ const mapStateToProps = (state, props) => {
+ const mapped = {
+ VendorsSummaryFilterDrawer: getVendorsBalanceSummaryFilterDrawer(
+ state,
+ props,
+ ),
+ };
+ return mapState ? mapState(mapped, state, props) : mapped;
+ };
+ return connect(mapStateToProps);
+};
diff --git a/client/src/containers/FinancialStatements/VendorsBalanceSummary/withVendorsBalanceSummaryActions.js b/client/src/containers/FinancialStatements/VendorsBalanceSummary/withVendorsBalanceSummaryActions.js
new file mode 100644
index 000000000..a2881f338
--- /dev/null
+++ b/client/src/containers/FinancialStatements/VendorsBalanceSummary/withVendorsBalanceSummaryActions.js
@@ -0,0 +1,9 @@
+import { connect } from 'react-redux';
+import { toggleVendorsBalanceSummaryFilterDrawer } from 'store/financialStatement/financialStatements.actions';
+
+const mapActionsToProps = (dispatch) => ({
+ toggleVendorSummaryFilterDrawer: (toggle) =>
+ dispatch(toggleVendorsBalanceSummaryFilterDrawer(toggle)),
+});
+
+export default connect(null, mapActionsToProps);