diff --git a/client/src/config/sidebarMenu.js b/client/src/config/sidebarMenu.js
index 41829981c..c97d0020a 100644
--- a/client/src/config/sidebarMenu.js
+++ b/client/src/config/sidebarMenu.js
@@ -1,4 +1,4 @@
- import React from 'react';
+import React from 'react';
import { FormattedMessage as T } from 'react-intl';
export default [
@@ -167,11 +167,11 @@ export default [
href: '/financial-reports/profit-loss-sheet',
},
{
- text: 'Receivable Aging Summary',
+ text: ,
href: '/financial-reports/receivable-aging-summary',
},
{
- text: 'Payable Aging Summary',
+ text: ,
href: '/financial-reports/payable-aging-summary',
},
],
diff --git a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummary.js b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummary.js
new file mode 100644
index 000000000..79b6e7025
--- /dev/null
+++ b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummary.js
@@ -0,0 +1,128 @@
+import React, { useEffect, useState, useCallback } from 'react';
+import { useIntl } from 'react-intl';
+import { queryCache, useQuery } from 'react-query';
+import moment from 'moment';
+
+import { FinancialStatement } from 'components';
+
+import DashboardInsider from 'components/Dashboard/DashboardInsider';
+import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
+
+import APAgingSummaryActionsBar from './APAgingSummaryActionsBar';
+import APAgingSummaryHeader from './APAgingSummaryHeader';
+import APAgingSummaryTable from './APAgingSummaryTable';
+
+import withSettings from 'containers/Settings/withSettings';
+import withDashboardActions from 'containers/Dashboard/withDashboardActions';
+import withAPAgingSummaryActions from './withAPAgingSummaryActions';
+import withAPAgingSummary from './withAPAgingSummary';
+import { transformFilterFormToQuery } from './common';
+
+import { compose } from 'utils';
+
+import 'style/pages/FinancialStatements/ARAgingSummary.scss';
+
+/**
+ * AP aging summary report.
+ */
+function APAgingSummary({
+ // #withSettings
+ organizationName,
+
+ // #withDashboardActions
+ changePageTitle,
+ setDashboardBackLink,
+
+ // #withAPAgingSummary
+ APAgingSummaryRefresh,
+
+ // #withAPAgingSummaryActions
+ requestPayableAgingSummary,
+ refreshAPAgingSummary,
+ toggleFilterAPAgingSummary,
+}) {
+ const { formatMessage } = useIntl();
+
+ const [query, setQuery] = useState({
+ asDate: moment().endOf('day').format('YYYY-MM-DD'),
+ agingBeforeDays: 30,
+ agingPeriods: 3,
+ });
+
+ // handle fetching payable aging summary report.
+ const fetchAPAgingSummarySheet = useQuery(
+ ['payable-aging-summary', query],
+ (key, _query) =>
+ requestPayableAgingSummary({
+ ...transformFilterFormToQuery(_query),
+ }),
+ { enable: true },
+ );
+
+ useEffect(() => {
+ changePageTitle(formatMessage({ id: 'payable_aging_summary' }));
+ }, [changePageTitle, formatMessage]);
+
+ useEffect(() => {
+ if (APAgingSummaryRefresh) {
+ queryCache.invalidateQueries('payable-aging-summary');
+ refreshAPAgingSummary(false);
+ }
+ }, [APAgingSummaryRefresh, refreshAPAgingSummary]);
+
+ useEffect(() => {
+ setDashboardBackLink(true);
+ return () => {
+ setDashboardBackLink(false);
+ };
+ }, [setDashboardBackLink]);
+
+ const handleFilterSubmit = (filter) => {
+ const _filter = {
+ ...filter,
+ asDate: moment(filter.asDate).format('YYYY-MM-DD'),
+ };
+ setQuery(_filter);
+ refreshAPAgingSummary(true);
+ toggleFilterAPAgingSummary(false);
+ };
+
+ const handleNumberFormatSubmit = (numberFormat) => {
+ setQuery({
+ ...query,
+ numberFormat,
+ });
+ refreshAPAgingSummary(true);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default compose(
+ withDashboardActions,
+ withAPAgingSummaryActions,
+ withSettings(({ organizationSettings }) => ({
+ organizationName: organizationSettings.name,
+ })),
+ withAPAgingSummary(({ APAgingSummaryRefresh }) => ({
+ APAgingSummaryRefresh,
+ })),
+)(APAgingSummary);
diff --git a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryActionsBar.js b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryActionsBar.js
new file mode 100644
index 000000000..5426ab7af
--- /dev/null
+++ b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryActionsBar.js
@@ -0,0 +1,123 @@
+import React from 'react';
+import {
+ NavbarDivider,
+ NavbarGroup,
+ Classes,
+ Button,
+ Popover,
+ PopoverInteractionKind,
+ Position,
+} from '@blueprintjs/core';
+import { safeInvoke } from '@blueprintjs/core/lib/esm/common/utils';
+
+import { FormattedMessage as T } from 'react-intl';
+import classNames from 'classnames';
+
+import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
+import { Icon } from 'components';
+import NumberFormatDropdown from 'components/NumberFormatDropdown';
+
+import withAPAgingSummary from './withAPAgingSummary';
+import withARAgingSummaryActions from './withAPAgingSummaryActions';
+
+import { compose } from 'utils';
+
+/**
+ * AP Aging summary sheet - Actions bar.
+ */
+function APAgingSummaryActionsBar({
+ //#withPayableAgingSummary
+ payableAgingFilter,
+ payableAgingLoading,
+
+ //#withARAgingSummaryActions
+ toggleFilterAPAgingSummary,
+ refreshAPAgingSummary,
+
+ //#ownProps
+ numberFormat,
+ onNumberFormatSubmit,
+}) {
+ const handleFilterToggleClick = () => toggleFilterAPAgingSummary();
+
+ // handle recalculate report button.
+ const handleRecalculateReport = () => refreshAPAgingSummary(true);
+
+ // handle number format submit.
+ const handleNumberFormatSubmit = (numberFormat) =>
+ safeInvoke(onNumberFormatSubmit, numberFormat);
+
+ return (
+
+
+ }
+ onClick={handleRecalculateReport}
+ icon={}
+ />
+
+ }
+ text={
+ payableAgingFilter ? (
+
+ ) : (
+
+ )
+ }
+ onClick={handleFilterToggleClick}
+ active={payableAgingFilter}
+ />
+
+
+ }
+ minimal={true}
+ interactionKind={PopoverInteractionKind.CLICK}
+ position={Position.BOTTOM_LEFT}
+ >
+ }
+ icon={}
+ />
+
+
+ }
+ icon={}
+ />
+
+
+ }
+ text={}
+ />
+ }
+ text={}
+ />
+
+
+ );
+}
+
+export default compose(
+ withARAgingSummaryActions,
+ withAPAgingSummary(
+ ({ payableAgingSummaryLoading, payableAgingSummaryFilter }) => ({
+ payableAgingLoading: payableAgingSummaryLoading,
+ payableAgingFilter: payableAgingSummaryFilter,
+ }),
+ ),
+)(APAgingSummaryActionsBar);
diff --git a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryHeader.js b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryHeader.js
new file mode 100644
index 000000000..91dea8281
--- /dev/null
+++ b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryHeader.js
@@ -0,0 +1,90 @@
+import React from 'react';
+import { FormattedMessage as T } from 'react-intl';
+import { Formik, Form, validateYupSchema } from 'formik';
+import * as Yup from 'yup';
+import moment from 'moment';
+import { Tabs, Tab, Button, Intent } from '@blueprintjs/core';
+
+import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
+import APAgingSummaryHeaderGeneral from './APAgingSummaryHeaderGeneral';
+
+import withAPAgingSummary from './withAPAgingSummary';
+import withAPAgingSummaryActions from './withAPAgingSummaryActions';
+
+import { compose } from 'utils';
+
+/**
+ * AP Aging Summary Report - Drawer Header.
+ */
+function APAgingSummaryHeader({
+ pageFilter,
+ onSubmitFilter,
+ payableAgingFilter,
+
+ // #withPayableAgingSummaryActions
+ toggleFilterAPAgingSummary,
+}) {
+ const validationSchema = Yup.object({
+ as_date: Yup.date().required().label('asDate'),
+ aging_days_before: Yup.number()
+ .required()
+ .integer()
+ .positive()
+ .label('agingBeforeDays'),
+ aging_periods: Yup.number()
+ .required()
+ .integer()
+ .positive()
+ .label('agingPeriods'),
+ });
+
+ // initial values.
+ const initialValues = {
+ as_date: moment(pageFilter.asDate).toDate(),
+ aging_days_before: 30,
+ aging_periods: 3,
+ };
+
+ // handle form submit.
+ const handleSubmit = (values, { setSubmitting }) => {
+ onSubmitFilter(values);
+ setSubmitting(false);
+ };
+
+ // handle cancel button click.
+ const handleCancelClick = () => toggleFilterAPAgingSummary();
+ return (
+
+
+
+
+
+ );
+}
+
+export default compose(
+ withAPAgingSummaryActions,
+ withAPAgingSummary(({ payableAgingSummaryFilter }) => ({
+ payableAgingFilter: payableAgingSummaryFilter,
+ })),
+)(APAgingSummaryHeader);
diff --git a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryHeaderGeneral.js b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryHeaderGeneral.js
new file mode 100644
index 000000000..4d72cfeb7
--- /dev/null
+++ b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryHeaderGeneral.js
@@ -0,0 +1,77 @@
+import React from 'react';
+import { FastField } from 'formik';
+import { DateInput } from '@blueprintjs/datetime';
+import { Intent, FormGroup, InputGroup, Position } from '@blueprintjs/core';
+import { FormattedMessage as T } from 'react-intl';
+import { Row, Col, FieldHint } from 'components';
+import {
+ momentFormatter,
+ tansformDateValue,
+ inputIntent,
+ handleDateChange,
+} from 'utils';
+
+/**
+ * AP Aging Summary - Drawer Header - General Fields.
+ */
+export default function APAgingSummaryHeaderGeneral() {
+ 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, meta: { error } }) => (
+ }
+ labelInfo={}
+ intent={inputIntent({ error })}
+ >
+
+
+ )}
+
+
+
+
+
+
+ {({ field, meta: { error } }) => (
+ }
+ labelInfo={}
+ intent={inputIntent({ error })}
+ >
+
+
+ )}
+
+
+
+
+ );
+}
diff --git a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryTable.js b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryTable.js
new file mode 100644
index 000000000..303d7c28b
--- /dev/null
+++ b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryTable.js
@@ -0,0 +1,103 @@
+import React, { useMemo, useCallback } from 'react';
+import { FormattedMessage as T, useIntl } from 'react-intl';
+import { DataTable } from 'components';
+import FinancialSheet from 'components/FinancialSheet';
+
+import withAPAgingSummary from './withAPAgingSummary';
+
+import { compose, getColumnWidth } from 'utils';
+
+/**
+ * AP aging summary table sheet.
+ */
+function APAgingSummaryTable({
+ //#withPayableAgingSummary
+ payableAgingColumns,
+ payableAgingRows,
+ payableAgingLoading,
+
+ //#ownProps
+ organizationName,
+}) {
+ const { formatMessage } = useIntl();
+ const agingColumns = useMemo(
+ () =>
+ payableAgingColumns.map((agingColumn) => {
+ return `${agingColumn.before_days} - ${
+ agingColumn.to_days || 'And Over'
+ }`;
+ }),
+ [payableAgingColumns],
+ );
+
+ const columns = useMemo(
+ () => [
+ {
+ Header: ,
+ accessor: 'name',
+ className: 'name',
+ width: 240,
+ sticky: 'left',
+ textOverview: true,
+ },
+ {
+ Header: ,
+ accessor: 'current',
+ className: 'current',
+ width: getColumnWidth(payableAgingRows, `current`, {
+ minWidth: 120,
+ }),
+ },
+
+ ...agingColumns.map((agingColumn, index) => ({
+ Header: agingColumn,
+ accessor: `aging-${index}`,
+ width: getColumnWidth(payableAgingRows, `aging-${index}`, {
+ minWidth: 120,
+ }),
+ })),
+ {
+ Header: ,
+ accessor: 'total',
+ width: getColumnWidth(payableAgingRows, 'total', {
+ minWidth: 120,
+ }),
+ },
+ ],
+ [payableAgingRows],
+ );
+ const rowClassNames = (row) => [`row-type--${row.original.rowType}`];
+
+ return (
+
+
+
+ );
+}
+
+export default compose(
+ withAPAgingSummary(
+ ({
+ payableAgingSummaryLoading,
+ payableAgingSummaryColumns,
+ payableAgingSummaryRows,
+ }) => ({
+ payableAgingLoading: payableAgingSummaryLoading,
+ payableAgingColumns: payableAgingSummaryColumns,
+ payableAgingRows: payableAgingSummaryRows,
+ }),
+ ),
+)(APAgingSummaryTable);
diff --git a/client/src/containers/FinancialStatements/APAgingSummary/common.js b/client/src/containers/FinancialStatements/APAgingSummary/common.js
new file mode 100644
index 000000000..0cb3aaea0
--- /dev/null
+++ b/client/src/containers/FinancialStatements/APAgingSummary/common.js
@@ -0,0 +1,5 @@
+import { transformToCamelCase, flatObject } from 'utils';
+
+export const transformFilterFormToQuery = (form) => {
+ return flatObject(transformToCamelCase(form));
+};
\ No newline at end of file
diff --git a/client/src/containers/FinancialStatements/APAgingSummary/withAPAgingSummary.js b/client/src/containers/FinancialStatements/APAgingSummary/withAPAgingSummary.js
new file mode 100644
index 000000000..9a0bed6b4
--- /dev/null
+++ b/client/src/containers/FinancialStatements/APAgingSummary/withAPAgingSummary.js
@@ -0,0 +1,35 @@
+import { connect } from 'react-redux';
+import {
+ getFinancialSheetFactory,
+ getFinancialSheetColumnsFactory,
+ getFinancialSheetTableRowsFactory,
+} from 'store/financialStatement/financialStatements.selectors';
+
+export default (mapState) => {
+ const mapStateToProps = (state, props) => {
+ const getAPAgingSheet = getFinancialSheetFactory('payableAgingSummary');
+ const getAPAgingSheetColumns = getFinancialSheetColumnsFactory(
+ 'payableAgingSummary',
+ );
+ const getAPAgingSheetRows = getFinancialSheetTableRowsFactory(
+ 'payableAgingSummary',
+ );
+
+ const {
+ loading,
+ filter,
+ refresh,
+ } = state.financialStatements.payableAgingSummary;
+
+ const mapped = {
+ payableAgingSummarySheet: getAPAgingSheet(state, props),
+ payableAgingSummaryColumns: getAPAgingSheetColumns(state, props),
+ payableAgingSummaryRows: getAPAgingSheetRows(state, props),
+ payableAgingSummaryLoading: loading,
+ payableAgingSummaryFilter: filter,
+ APAgingSummaryRefresh: refresh,
+ };
+ return mapState ? mapState(mapped, state, props) : mapped;
+ };
+ return connect(mapStateToProps);
+};
diff --git a/client/src/containers/FinancialStatements/APAgingSummary/withAPAgingSummaryActions.js b/client/src/containers/FinancialStatements/APAgingSummary/withAPAgingSummaryActions.js
new file mode 100644
index 000000000..87cefd3d8
--- /dev/null
+++ b/client/src/containers/FinancialStatements/APAgingSummary/withAPAgingSummaryActions.js
@@ -0,0 +1,18 @@
+import { connect } from 'react-redux';
+import {
+ fetchPayableAginSummary,
+ payableAgingSummaryRefresh,
+} from 'store/financialStatement/financialStatements.actions';
+
+const mapActionsToProps = (dispatch) => ({
+ requestPayableAgingSummary: (query) =>
+ dispatch(fetchPayableAginSummary({ query })),
+ refreshAPAgingSummary: (refresh) =>
+ dispatch(payableAgingSummaryRefresh(refresh)),
+ toggleFilterAPAgingSummary: () =>
+ dispatch({
+ type: 'PAYABLE_AGING_SUMMARY_FILTER_TOGGLE',
+ }),
+});
+
+export default connect(null, mapActionsToProps);
diff --git a/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummary.js b/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummary.js
index 54faeefe9..194bb575d 100644
--- a/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummary.js
+++ b/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummary.js
@@ -97,16 +97,17 @@ function ReceivableAgingSummarySheet({
const handleNumberFormatSubmit = (numberFormat) => {
setQuery({
...query,
- numberFormat
+ numberFormat,
});
refreshARAgingSummary(true);
};
-
+ console.log(query, 'EE');
return (
+ onNumberFormatSubmit={handleNumberFormatSubmit}
+ />
diff --git a/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryActionsBar.js b/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryActionsBar.js
index 9059987cc..e511ad802 100644
--- a/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryActionsBar.js
+++ b/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryActionsBar.js
@@ -48,7 +48,6 @@ function ARAgingSummaryActionsBar({
const handleNumberFormatSubmit = (numberFormat) => {
safeInvoke(onNumberFormatSubmit, numberFormat);
};
-
return (
@@ -98,7 +97,7 @@ function ARAgingSummaryActionsBar({
className={Classes.MINIMAL}
text={}
icon={}
- />
+ />