diff --git a/client/src/components/CustomersMultiSelect.js b/client/src/components/ContactsMultiSelect.js similarity index 52% rename from client/src/components/CustomersMultiSelect.js rename to client/src/components/ContactsMultiSelect.js index 1afa5a2f2..3d622af9f 100644 --- a/client/src/components/CustomersMultiSelect.js +++ b/client/src/components/ContactsMultiSelect.js @@ -4,65 +4,65 @@ import { omit } from 'lodash'; import MultiSelect from 'components/MultiSelect'; import { FormattedMessage as T } from 'react-intl'; -export default function CustomersMultiSelect({ - customers, +export default function ContactsMultiSelect({ + contacts, defaultText = , buttonProps, - onCustomerSelected, + onCustomerSelected: onContactSelected, ...selectProps }) { - const [selectedCustomers, setSelectedCustomers] = useState({}); + const [selectedContacts, setSelectedContacts] = useState({}); - const isCustomerSelect = useCallback( - (id) => typeof selectedCustomers[id] !== 'undefined', - [selectedCustomers], + const isContactSelect = useCallback( + (id) => typeof selectedContacts[id] !== 'undefined', + [selectedContacts], ); - const customerRenderer = useCallback( - (customer, { handleClick }) => ( + const contactRenderer = useCallback( + (contact, { handleClick }) => ( ), - [isCustomerSelect], + [isContactSelect], ); - const countSelected = useMemo(() => Object.values(selectedCustomers).length, [ - selectedCustomers, + const countSelected = useMemo(() => Object.values(selectedContacts).length, [ + selectedContacts, ]); const onContactSelect = useCallback( ({ id }) => { const selected = { - ...(isCustomerSelect(id) + ...(isContactSelect(id) ? { - ...omit(selectedCustomers, [id]), + ...omit(selectedContacts, [id]), } : { - ...selectedCustomers, + ...selectedContacts, [id]: true, }), }; - setSelectedCustomers({ ...selected }); - onCustomerSelected && onCustomerSelected(selected); + setSelectedContacts({ ...selected }); + onContactSelected && onContactSelected(selected); }, [ - setSelectedCustomers, - selectedCustomers, - isCustomerSelect, - onCustomerSelected, + setSelectedContacts, + selectedContacts, + isContactSelect, + onContactSelected, ], ); return ( } - itemRenderer={customerRenderer} + itemRenderer={contactRenderer} popoverProps={{ minimal: true }} filterable={true} onItemSelect={onContactSelect} diff --git a/client/src/components/index.js b/client/src/components/index.js index 18ade28b4..19befa37f 100644 --- a/client/src/components/index.js +++ b/client/src/components/index.js @@ -43,7 +43,7 @@ import DashboardCard from './Dashboard/DashboardCard'; import InputPrependText from './Forms/InputPrependText'; import PageFormBigNumber from './PageFormBigNumber'; import AccountsMultiSelect from './AccountsMultiSelect'; -import CustomersMultiSelect from './CustomersMultiSelect'; +import ContactsMultiSelect from './ContactsMultiSelect'; import Skeleton from './Skeleton' import ContextMenu from './ContextMenu' import TableFastCell from './Datatable/TableFastCell'; @@ -99,7 +99,7 @@ export { PageFormBigNumber, AccountsMultiSelect, DataTableEditable, - CustomersMultiSelect, + ContactsMultiSelect, TableFastCell, Skeleton, ContextMenu, diff --git a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummary.js b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummary.js index 79b6e7025..687b4dc7b 100644 --- a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummary.js +++ b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummary.js @@ -1,27 +1,20 @@ -import React, { useEffect, useState, useCallback } from 'react'; -import { useIntl } from 'react-intl'; -import { queryCache, useQuery } from 'react-query'; +import React, { useState, useCallback, useEffect } from 'react'; import moment from 'moment'; +import 'style/pages/FinancialStatements/ARAgingSummary.scss'; + 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 APAgingSummaryActionsBar from './APAgingSummaryActionsBar'; import APAgingSummaryTable from './APAgingSummaryTable'; +import DashboardPageContent from 'components/Dashboard/DashboardPageContent'; +import { APAgingSummaryProvider } from './APAgingSummaryProvider'; 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 withAPAgingSummaryActions from './withAPAgingSummaryActions' import { compose } from 'utils'; -import 'style/pages/FinancialStatements/ARAgingSummary.scss'; - /** * AP aging summary report. */ @@ -29,82 +22,47 @@ function APAgingSummary({ // #withSettings organizationName, - // #withDashboardActions - changePageTitle, - setDashboardBackLink, - - // #withAPAgingSummary - APAgingSummaryRefresh, - // #withAPAgingSummaryActions - requestPayableAgingSummary, - refreshAPAgingSummary, - toggleFilterAPAgingSummary, + toggleAPAgingSummaryFilterDrawer: toggleDisplayFilterDrawer, }) { - const { formatMessage } = useIntl(); - - const [query, setQuery] = useState({ + const [filter, setFilter] = 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) => { + // Handle filter submit. + const handleFilterSubmit = useCallback((filter) => { const _filter = { ...filter, asDate: moment(filter.asDate).format('YYYY-MM-DD'), }; - setQuery(_filter); - refreshAPAgingSummary(true); - toggleFilterAPAgingSummary(false); - }; + setFilter(_filter); + }, []); + // Handle number format submit. const handleNumberFormatSubmit = (numberFormat) => { - setQuery({ - ...query, + setFilter({ + ...filter, numberFormat, }); - refreshAPAgingSummary(true); }; + // Hide the report filter drawer once the page unmount. + useEffect(() => () => { + toggleDisplayFilterDrawer(false); + }, [toggleDisplayFilterDrawer]) + return ( - +
@@ -112,17 +70,13 @@ function APAgingSummary({
-
+ ); } export default compose( - withDashboardActions, - withAPAgingSummaryActions, withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings.name, - })), - withAPAgingSummary(({ APAgingSummaryRefresh }) => ({ - APAgingSummaryRefresh, + organizationName: organizationSettings?.name, })), + withAPAgingSummaryActions )(APAgingSummary); diff --git a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryActionsBar.js b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryActionsBar.js index 5426ab7af..fa000b9bd 100644 --- a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryActionsBar.js +++ b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryActionsBar.js @@ -8,7 +8,6 @@ import { 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'; @@ -17,36 +16,42 @@ import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'; import { Icon } from 'components'; import NumberFormatDropdown from 'components/NumberFormatDropdown'; +import { useAPAgingSummaryContext } from './APAgingSummaryProvider'; import withAPAgingSummary from './withAPAgingSummary'; -import withARAgingSummaryActions from './withAPAgingSummaryActions'; +import withAPAgingSummaryActions from './withAPAgingSummaryActions'; -import { compose } from 'utils'; +import { saveInvoke, compose } from 'utils'; /** * AP Aging summary sheet - Actions bar. */ function APAgingSummaryActionsBar({ - //#withPayableAgingSummary + // #withPayableAgingSummary payableAgingFilter, - payableAgingLoading, - //#withARAgingSummaryActions - toggleFilterAPAgingSummary, - refreshAPAgingSummary, + // #withARAgingSummaryActions + toggleAPAgingSummaryFilterDrawer: toggleFilterDrawerDisplay, //#ownProps numberFormat, onNumberFormatSubmit, }) { - const handleFilterToggleClick = () => toggleFilterAPAgingSummary(); + const { isAPAgingFetching, refetch } = useAPAgingSummaryContext(); + + const handleFilterToggleClick = () => { + toggleFilterDrawerDisplay(); + } // handle recalculate report button. - const handleRecalculateReport = () => refreshAPAgingSummary(true); + const handleRecalculateReport = () => { + refetch(); + } // handle number format submit. - const handleNumberFormatSubmit = (numberFormat) => - safeInvoke(onNumberFormatSubmit, numberFormat); - + const handleNumberFormatSubmit = (numberFormat) => { + saveInvoke(onNumberFormatSubmit, numberFormat); + } + return ( @@ -76,7 +81,7 @@ function APAgingSummaryActionsBar({ } minimal={true} @@ -113,11 +118,8 @@ function APAgingSummaryActionsBar({ } export default compose( - withARAgingSummaryActions, - withAPAgingSummary( - ({ payableAgingSummaryLoading, payableAgingSummaryFilter }) => ({ - payableAgingLoading: payableAgingSummaryLoading, - payableAgingFilter: payableAgingSummaryFilter, - }), - ), + withAPAgingSummaryActions, + withAPAgingSummary(({ APAgingSummaryFilterDrawer }) => ({ + isFilterDrawerOpen: APAgingSummaryFilterDrawer + })) )(APAgingSummaryActionsBar); diff --git a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryHeader.js b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryHeader.js index 91dea8281..6ad67965e 100644 --- a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryHeader.js +++ b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryHeader.js @@ -1,6 +1,6 @@ import React from 'react'; import { FormattedMessage as T } from 'react-intl'; -import { Formik, Form, validateYupSchema } from 'formik'; +import { Formik, Form } from 'formik'; import * as Yup from 'yup'; import moment from 'moment'; import { Tabs, Tab, Button, Intent } from '@blueprintjs/core'; @@ -17,12 +17,15 @@ import { compose } from 'utils'; * AP Aging Summary Report - Drawer Header. */ function APAgingSummaryHeader({ + // #ownProps pageFilter, onSubmitFilter, - payableAgingFilter, - // #withPayableAgingSummaryActions - toggleFilterAPAgingSummary, + // #withAPAgingSummaryActions + toggleAPAgingSummaryFilterDrawer: toggleFilterDrawerDisplay, + + // #withAPAgingSummary + isFilterDrawerOpen }) { const validationSchema = Yup.object({ as_date: Yup.date().required().label('asDate'), @@ -38,23 +41,32 @@ function APAgingSummaryHeader({ .label('agingPeriods'), }); - // initial values. + // Initial values. const initialValues = { as_date: moment(pageFilter.asDate).toDate(), aging_days_before: 30, aging_periods: 3, }; - // handle form submit. + // Handle form submit. const handleSubmit = (values, { setSubmitting }) => { onSubmitFilter(values); + toggleFilterDrawerDisplay(false); setSubmitting(false); }; // handle cancel button click. - const handleCancelClick = () => toggleFilterAPAgingSummary(); + const handleCancelClick = () => { + toggleFilterDrawerDisplay(false); + }; + + // Handle the drawer closing. + const handleDrawerClose = () => { + toggleFilterDrawerDisplay(false); + }; + return ( - + ({ - payableAgingFilter: payableAgingSummaryFilter, + withAPAgingSummary(({ APAgingSummaryFilterDrawer }) => ({ + isFilterDrawerOpen: APAgingSummaryFilterDrawer, })), )(APAgingSummaryHeader); diff --git a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryHeaderGeneral.js b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryHeaderGeneral.js index 4d72cfeb7..a6b8589fb 100644 --- a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryHeaderGeneral.js +++ b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryHeaderGeneral.js @@ -1,9 +1,17 @@ import React from 'react'; import { FastField } from 'formik'; import { DateInput } from '@blueprintjs/datetime'; -import { Intent, FormGroup, InputGroup, Position } from '@blueprintjs/core'; +import { + Intent, + FormGroup, + InputGroup, + Position, + Classes, +} from '@blueprintjs/core'; import { FormattedMessage as T } from 'react-intl'; -import { Row, Col, FieldHint } from 'components'; +import classNames from 'classnames'; +import { ContactsMultiSelect, Row, Col, FieldHint } from 'components'; +import { useAPAgingSummaryContext } from './APAgingSummaryProvider'; import { momentFormatter, tansformDateValue, @@ -15,6 +23,7 @@ import { * AP Aging Summary - Drawer Header - General Fields. */ export default function APAgingSummaryHeaderGeneral() { + const { vendors } = useAPAgingSummaryContext(); return (
@@ -72,6 +81,19 @@ export default function APAgingSummaryHeaderGeneral() { + + + } + className={classNames('form-group--select-list', Classes.FILL)} + > + } + contacts={vendors} + /> + + +
); } diff --git a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryProvider.js b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryProvider.js new file mode 100644 index 000000000..0a5629729 --- /dev/null +++ b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryProvider.js @@ -0,0 +1,49 @@ +import React, { useMemo, createContext, useContext } from 'react'; +import DashboardInsider from 'components/Dashboard/DashboardInsider'; +import { useAPAgingSummaryReport,useARAgingSummaryReport , useVendors } from 'hooks/query'; +import { transformFilterFormToQuery } from '../common'; + +const APAgingSummaryContext = createContext(); + +/** + * A/P aging summary provider. + */ +function APAgingSummaryProvider({ filter, ...props }) { + // Transformers the filter from to the Url query. + const query = useMemo(() => transformFilterFormToQuery(filter), [filter]); + + const { + data: APAgingSummary, + isLoading: isAPAgingLoading, + isFetching: isAPAgingFetching, + refetch, + } = useAPAgingSummaryReport(query); + + // Retrieve the vendors list. + const { + data: { vendors }, + isFetching: isVendorsLoading, + } = useVendors(); + + const provider = { + APAgingSummary, + vendors, + + isAPAgingLoading, + isAPAgingFetching, + isVendorsLoading, + refetch, + }; + + return ( + + + + ); +} + + +const useAPAgingSummaryContext = () => useContext(APAgingSummaryContext); + + +export { APAgingSummaryProvider, useAPAgingSummaryContext }; diff --git a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryTable.js b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryTable.js index 303d7c28b..bd433e254 100644 --- a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryTable.js +++ b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummaryTable.js @@ -1,71 +1,29 @@ -import React, { useMemo, useCallback } from 'react'; +import React, { 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'; +import { useAPAgingSummaryContext } from './APAgingSummaryProvider'; +import { useAPAgingSummaryColumns } from './components'; /** * AP aging summary table sheet. */ -function APAgingSummaryTable({ - //#withPayableAgingSummary - payableAgingColumns, - payableAgingRows, - payableAgingLoading, - +export default function APAgingSummaryTable({ //#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, - }), - }, + // AP aging summary report content. + const { + APAgingSummary: { tableRows }, + isAPAgingFetching, + } = useAPAgingSummaryContext(); + + // AP aging summary columns. + const columns = useAPAgingSummaryColumns(); - ...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 ( @@ -74,12 +32,12 @@ function APAgingSummaryTable({ name={'payable-aging-summary'} sheetType={formatMessage({ id: 'payable_aging_summary' })} asDate={new Date()} - loading={payableAgingLoading} + loading={isAPAgingFetching} > ); } - -export default compose( - withAPAgingSummary( - ({ - payableAgingSummaryLoading, - payableAgingSummaryColumns, - payableAgingSummaryRows, - }) => ({ - payableAgingLoading: payableAgingSummaryLoading, - payableAgingColumns: payableAgingSummaryColumns, - payableAgingRows: payableAgingSummaryRows, - }), - ), -)(APAgingSummaryTable); diff --git a/client/src/containers/FinancialStatements/APAgingSummary/components.js b/client/src/containers/FinancialStatements/APAgingSummary/components.js new file mode 100644 index 000000000..0e1c1afec --- /dev/null +++ b/client/src/containers/FinancialStatements/APAgingSummary/components.js @@ -0,0 +1,56 @@ +import React, { useMemo } from 'react'; +import { useAPAgingSummaryContext } from './APAgingSummaryProvider'; +import { getColumnWidth } from 'utils'; +import { FormattedMessage as T } from 'react-intl'; + +/** + * Retrieve AP aging summary columns. + */ +export const useAPAgingSummaryColumns = () => { + const { + APAgingSummary: { tableRows, columns }, + } = useAPAgingSummaryContext(); + + const agingColumns = React.useMemo(() => { + return columns.map( + (agingColumn) => + `${agingColumn.before_days} - ${agingColumn.to_days || 'And Over'}`, + ); + }, [columns]); + + return useMemo( + () => [ + { + Header: , + accessor: 'name', + className: 'name', + width: 240, + sticky: 'left', + textOverview: true, + }, + { + Header: , + accessor: 'current', + className: 'current', + width: getColumnWidth(tableRows, `current`, { + minWidth: 120, + }), + }, + ...agingColumns.map((agingColumn, index) => ({ + Header: agingColumn, + accessor: `aging-${index}`, + width: getColumnWidth(tableRows, `aging-${index}`, { + minWidth: 120, + }), + })), + { + Header: , + accessor: 'total', + width: getColumnWidth(tableRows, 'total', { + minWidth: 120, + }), + }, + ], + [tableRows, agingColumns], + ); +}; diff --git a/client/src/containers/FinancialStatements/APAgingSummary/withAPAgingSummary.js b/client/src/containers/FinancialStatements/APAgingSummary/withAPAgingSummary.js index 9a0bed6b4..912904c8e 100644 --- a/client/src/containers/FinancialStatements/APAgingSummary/withAPAgingSummary.js +++ b/client/src/containers/FinancialStatements/APAgingSummary/withAPAgingSummary.js @@ -1,33 +1,15 @@ import { connect } from 'react-redux'; import { - getFinancialSheetFactory, - getFinancialSheetColumnsFactory, - getFinancialSheetTableRowsFactory, + APAgingSummaryFilterDrawerSelector, } 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, + APAgingSummaryFilterDrawer: APAgingSummaryFilterDrawerSelector( + state, + props, + ), }; return mapState ? mapState(mapped, state, props) : mapped; }; diff --git a/client/src/containers/FinancialStatements/APAgingSummary/withAPAgingSummaryActions.js b/client/src/containers/FinancialStatements/APAgingSummary/withAPAgingSummaryActions.js index 87cefd3d8..b1572b493 100644 --- a/client/src/containers/FinancialStatements/APAgingSummary/withAPAgingSummaryActions.js +++ b/client/src/containers/FinancialStatements/APAgingSummary/withAPAgingSummaryActions.js @@ -1,18 +1,9 @@ import { connect } from 'react-redux'; -import { - fetchPayableAginSummary, - payableAgingSummaryRefresh, -} from 'store/financialStatement/financialStatements.actions'; +import { toggleAPAgingSummaryFilterDrawer } 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', - }), + toggleAPAgingSummaryFilterDrawer: (toggle) => + dispatch(toggleAPAgingSummaryFilterDrawer(toggle)), }); export default connect(null, mapActionsToProps); diff --git a/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryActionsBar.js b/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryActionsBar.js index af52486c5..3618b14bb 100644 --- a/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryActionsBar.js +++ b/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryActionsBar.js @@ -39,7 +39,7 @@ function ARAgingSummaryActionsBar({ const { isARAgingFetching, refetch } = useARAgingSummaryContext(); const handleFilterToggleClick = () => { - toggleDisplayFilterDrawer(false); + toggleDisplayFilterDrawer(); }; // Handles re-calculate report button. diff --git a/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryHeader.js b/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryHeader.js index e25aaef97..8b2e0ec18 100644 --- a/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryHeader.js +++ b/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummaryHeader.js @@ -25,7 +25,7 @@ function ARAgingSummaryHeader({ toggleARAgingSummaryFilterDrawer: toggleFilterDrawerDisplay, // #withARAgingSummary - isFilterDrawerOpen + isFilterDrawerOpen, }) { const validationSchema = Yup.object().shape({ asDate: Yup.date().required().label('asDate'), @@ -53,13 +53,22 @@ function ARAgingSummaryHeader({ toggleFilterDrawerDisplay(false); setSubmitting(false); }; + // Handle cancel button click. const handleCancelClick = () => { toggleFilterDrawerDisplay(false); }; + + // Handle the drawer close. + const handleDrawerClose = () => { + toggleFilterDrawerDisplay(false); + }; return ( - + } className={classNames('form-group--select-list', Classes.FILL)} > - + diff --git a/client/src/containers/FinancialStatements/reducers.js b/client/src/containers/FinancialStatements/reducers.js index 775a2c074..6af950eea 100644 --- a/client/src/containers/FinancialStatements/reducers.js +++ b/client/src/containers/FinancialStatements/reducers.js @@ -261,3 +261,38 @@ export const ARAgingSummaryTableRowsMapper = (sheet, total) => { }, ]; }; + +export const APAgingSummaryTableRowsMapper = (sheet, total) => { + const rows = []; + + const mapAging = (agingPeriods) => { + return agingPeriods.reduce((acc, aging, index) => { + acc[`aging-${index}`] = aging.total.formatted_amount; + return acc; + }, {}); + }; + sheet.vendors.forEach((vendor) => { + const agingRow = mapAging(vendor.aging); + + rows.push({ + rowType: 'vendor', + name: vendor.vendor_name, + ...agingRow, + current: vendor.current.formatted_amount, + total: vendor.total.formatted_amount, + }); + }); + if (rows.length <= 0) { + return []; + } + return [ + ...rows, + { + name: '', + rowType: 'total', + current: sheet.total.current.formatted_amount, + ...mapAging(sheet.total.aging), + total: sheet.total.total.formatted_amount, + }, + ]; +}; diff --git a/client/src/hooks/query/financialReports.js b/client/src/hooks/query/financialReports.js index b7bdf7d51..b7b03fdaa 100644 --- a/client/src/hooks/query/financialReports.js +++ b/client/src/hooks/query/financialReports.js @@ -6,10 +6,11 @@ import { profitLossSheetReducer, generalLedgerTableRowsReducer, journalTableRowsReducer, - ARAgingSummaryTableRowsMapper + ARAgingSummaryTableRowsMapper, + APAgingSummaryTableRowsMapper } from 'containers/FinancialStatements/reducers'; import useApiRequest from '../useRequest'; - + /** * Retrieve balance sheet. */ @@ -27,7 +28,7 @@ export function useBalanceSheet(query, props) { tableRows: balanceSheetRowsReducer(res.data.data), ...res.data, }), - ...props + ...props, }, ); @@ -69,8 +70,8 @@ export function useTrialBalanceSheet(query, props) { tableRows: [], data: [], query: {}, - }) - } + }), + }; } /** @@ -101,7 +102,7 @@ export function useProfitLossSheet(query, props) { columns: [], query: {}, }), - } + }; } /** @@ -132,7 +133,7 @@ export function useGeneralLedgerSheet(query, props) { data: {}, query: {}, }), - } + }; } /** @@ -143,12 +144,11 @@ export function useJournalSheet(query, props) { const states = useQuery( ['FINANCIAL-REPORT', 'JOURNAL', query], - () => - apiRequest.get('/financial_statements/journal', { params: query }), + () => apiRequest.get('/financial_statements/journal', { params: query }), { select: (res) => ({ tableRows: journalTableRowsReducer(res.data.data), - ...res.data, + ...res.data, }), ...props, }, @@ -160,8 +160,8 @@ export function useJournalSheet(query, props) { data: {}, tableRows: [], query: {}, - }) - } + }), + }; } /** @@ -185,7 +185,7 @@ export function useARAgingSummaryReport(query, props) { customers: res.data.data.customers, total: res.data.data.total, columns: res.data.columns, - }), + }), }), initialData: { data: { @@ -194,11 +194,50 @@ export function useARAgingSummaryReport(query, props) { total: {}, }, columns: [], - tableRows: [] - } + tableRows: [], + }, }, initialDataUpdatedAt: 0, - ...props + ...props, + }, + ); +} + +/** + * Retrieve AP aging summary report. + */ +export function useAPAgingSummaryReport(query, props) { + const apiRequest = useApiRequest(); + + return useQuery( + ['FINANCIAL-REPORT', 'AP-AGING-SUMMARY', query], + () => + apiRequest.get('/financial_statements/payable_aging_summary', { + params: query, + }), + { + select: (res) => ({ + columns: res.data.columns, + data: res.data.data, + query: res.data.query, + tableRows: APAgingSummaryTableRowsMapper({ + vendors: res.data.data.vendors, + total: res.data.data.total, + columns: res.data.columns, + }), + }), + initialData: { + data: { + data: { + vendors: [], + total: {}, + }, + columns: [], + tableRows: [], + }, + }, + initialDataUpdatedAt: 0, + ...props, }, ); } diff --git a/client/src/lang/en/index.js b/client/src/lang/en/index.js index 5b9e390d8..4e436e30d 100644 --- a/client/src/lang/en/index.js +++ b/client/src/lang/en/index.js @@ -961,6 +961,7 @@ export default { adjustment_reasons: 'Adjustment reasons', specific_customers: 'Specific Customers', all_customers: 'All Customers', + all_vendors: 'All Vendors', selected_customers: '{count} Selected Customers', transaction_number: 'Transaction #', running_balance: 'Running balance', @@ -976,4 +977,6 @@ export default { receipt_paper: 'Receipt Paper', payable_aging_summary: 'Payable Aging Summary', payment_receive_paper: 'Payment Receive Paper', + specific_vendors: 'Specific Vendors', + }; diff --git a/client/src/routes/dashboard.js b/client/src/routes/dashboard.js index c5a423dae..db593d92f 100644 --- a/client/src/routes/dashboard.js +++ b/client/src/routes/dashboard.js @@ -159,13 +159,16 @@ export default [ backLink: true, sidebarShrink: true, }, - // { - // path: '/financial-reports/payable-aging-summary', - // component: lazy(() => - // import('containers/FinancialStatements/APAgingSummary/APAgingSummary'), - // ), - // breadcrumb: 'Payable Aging Summary', - // }, + { + path: '/financial-reports/payable-aging-summary', + component: lazy(() => + import('containers/FinancialStatements/APAgingSummary/APAgingSummary'), + ), + breadcrumb: 'Payable Aging Summary', + pageTitle: formatMessage({ id: 'payable_aging_summary' }), + backLink: true, + sidebarShrink: true, + }, { path: `/financial-reports/journal-sheet`, component: lazy(() => diff --git a/client/src/store/financialStatement/financialStatements.actions.js b/client/src/store/financialStatement/financialStatements.actions.js index 2060aefbb..f961a7eda 100644 --- a/client/src/store/financialStatement/financialStatements.actions.js +++ b/client/src/store/financialStatement/financialStatements.actions.js @@ -76,4 +76,17 @@ export function toggleARAgingSummaryFilterDrawer(toggle) { toggle, } }; +} + +/** + * Toggles display of the AP aging summary filter drawer. + * @param {boolean} toggle - + */ +export function toggleAPAgingSummaryFilterDrawer(toggle) { + return { + type: `${t.AP_AGING_SUMMARY}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`, + payload: { + toggle, + } + }; } \ No newline at end of file diff --git a/client/src/store/financialStatement/financialStatements.mappers.js b/client/src/store/financialStatement/financialStatements.mappers.js index e771198cf..7377bbb23 100644 --- a/client/src/store/financialStatement/financialStatements.mappers.js +++ b/client/src/store/financialStatement/financialStatements.mappers.js @@ -137,6 +137,41 @@ export const ARAgingSummaryTableRowsMapper = (sheet, total) => { ]; }; +export const APAgingSummaryTableRowsMapper = (sheet, total) => { + const rows = []; + + const mapAging = (agingPeriods) => { + return agingPeriods.reduce((acc, aging, index) => { + acc[`aging-${index}`] = aging.total.formatted_amount; + return acc; + }, {}); + }; + sheet.vendors.forEach((vendor) => { + const agingRow = mapAging(vendor.aging); + + rows.push({ + rowType: 'vendor', + name: vendor.vendor_name, + ...agingRow, + current: vendor.current.formatted_amount, + total: vendor.total.formatted_amount, + }); + }); + if (rows.length <= 0) { + return []; + } + return [ + ...rows, + { + name: '', + rowType: 'total', + current: sheet.total.current.formatted_amount, + ...mapAging(sheet.total.aging), + total: sheet.total.total.formatted_amount, + }, + ]; +}; + export const mapTrialBalanceSheetToRows = (sheet) => { const results = []; diff --git a/client/src/store/financialStatement/financialStatements.selectors.js b/client/src/store/financialStatement/financialStatements.selectors.js index eee24b610..5954c8209 100644 --- a/client/src/store/financialStatement/financialStatements.selectors.js +++ b/client/src/store/financialStatement/financialStatements.selectors.js @@ -34,6 +34,11 @@ export const ARAgingSummaryFilterDrawerSelector = (state) => { return filterDrawerByTypeSelector('ARAgingSummary')(state); }; +export const APAgingSummaryFilterDrawerSelector = (state) => { + return filterDrawerByTypeSelector('APAgingSummary')(state); +} + + /** * Retrieve balance sheet filter drawer. */ @@ -94,6 +99,17 @@ export const getARAgingSummaryFilterDrawer = createSelector( }, ); + +/** + * Retrieve whether display AR aging summary drawer filter. + */ +export const getAPAgingSummaryFilterDrawer = createSelector( + APAgingSummaryFilterDrawerSelector, + (isOpen) => { + return isOpen; + }, +); + /** * Retrieve financial statement query by the given sheet index. */