diff --git a/client/src/config/financialReportsMenu.js b/client/src/config/financialReportsMenu.js index f32beaf2f..fc82eef1b 100644 --- a/client/src/config/financialReportsMenu.js +++ b/client/src/config/financialReportsMenu.js @@ -10,7 +10,8 @@ export const financialReportMenus = [ }, { title: 'Trial Balance Sheet', - desc: 'Summarizes the credit and debit balance of each account in your chart of accounts at a specific point in time.', + desc: + 'Summarizes the credit and debit balance of each account in your chart of accounts at a specific point in time.', link: '/financial-reports/trial-balance-sheet', }, { @@ -21,22 +22,26 @@ export const financialReportMenus = [ }, { title: 'Profit/Loss Report', - desc: "Reports the revenues, costs and expenses incurred during a specific point in time with comparison period(s).", + desc: + 'Reports the revenues, costs and expenses incurred during a specific point in time with comparison period(s).', link: '/financial-reports/profit-loss-sheet', }, { title: 'General Ledger Report', - desc: "Reports every transaction going in and out of your accounts and organized by accounts and date to monitoring activity of accounts.", + desc: + 'Reports every transaction going in and out of your accounts and organized by accounts and date to monitoring activity of accounts.', link: '/financial-reports/general-ledger', }, { title: 'Receivable Aging Summary', - desc: "Summarize total unpaid balances of customers invoices with number of days the unpaid invoice is overdue.", + desc: + 'Summarize total unpaid balances of customers invoices with number of days the unpaid invoice is overdue.', link: '/financial-reports/receivable-aging-summary', }, { title: 'Payable Aging Summary', - desc: "Summarize total unpaid balances of vendors purchase invoices with the number of days the unpaid invoice is overdue.", + desc: + 'Summarize total unpaid balances of vendors purchase invoices with the number of days the unpaid invoice is overdue.', link: '/financial-reports/payable-aging-summary', }, ], @@ -54,16 +59,37 @@ export const SalesAndPurchasesReportMenus = [ link: '/financial-reports/purchases-by-items', }, { - title: 'Sales by Items', + title: 'Sales By Items', desc: - "Summarize the business’s sold items quantity, income and average income rate of each item during a specific point in time.", + 'Summarize the business’s sold items quantity, income and average income rate of each item during a specific point in time.', link: '/financial-reports/sales-by-items', }, { title: 'Inventory valuation', - desc: 'Summarize the business’s purchase items quantity, cost and average cost rate of each item during a specific point in time.', + desc: + 'Summarize the business’s purchase items quantity, cost and average cost rate of each item during a specific point in time.', link: '/financial-reports/inventory-valuation', }, + { + title: 'Customer Balance summary', + desc: '', + link: '/financial-reports/customers-balance-summary', + }, + { + title: 'Vendors Balance summary', + desc: '', + link: '/financial-reports/vendors-balance-summary', + }, + { + title: 'Customers Transactions', + desc: '', + link: '/financial-reports/transactions-by-customers', + }, + { + title: 'Vendors Transactions', + desc: '', + link: '/financial-reports/transactions-by-vendors', + }, ], }, ]; diff --git a/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummary.js b/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummary.js new file mode 100644 index 000000000..f45480599 --- /dev/null +++ b/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummary.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 CustomersBalanceSummaryActionsBar from './CustomersBalanceSummaryActionsBar'; +import CustomersBalanceSummaryHeader from './CustomersBalanceSummaryHeader'; +import CustomersBalanceSummaryTable from './CustomersBalanceSummaryTable'; + +import { CustomersBalanceLoadingBar } from './components'; +import { CustomersBalanceSummaryProvider } from './CustomersBalanceSummaryProvider'; +import withCustomersBalanceSummaryActions from './withCustomersBalanceSummaryActions'; + +import withSettings from 'containers/Settings/withSettings'; + +import { compose } from 'redux'; + +/** + * Customers Balance summary. + */ +function CustomersBalanceSummary({ + // #withPreferences + organizationName, + + // #withCustomersBalanceSummaryActions + toggleCustomerBalanceFilterDrawer, +}) { + + const [filter, setFilter] = useState({ + asDate: moment().endOf('day').format('YYYY-MM-DD'), + }); + + // Handle re-fetch customers balance summary after filter change. + const handleFilterSubmit = (filter) => { + const _filter = { + ...filter, + asDate: moment(filter.asDate).format('YYYY-MM-DD'), + }; + setFilter({ ..._filter }); + }; + + // Handle number format. + const handleNumberFormat = (values) => { + setFilter({ + ...filter, + numberFormat: values, + }); + }; + + useEffect( + () => () => { + toggleCustomerBalanceFilterDrawer(false); + }, + [toggleCustomerBalanceFilterDrawer], + ); + + return ( + + + + + + + +
+ +
+
+
+
+ ); +} +export default compose( + withSettings(({ organizationSettings }) => ({ + organizationName: organizationSettings.name, + })), + withCustomersBalanceSummaryActions, +)(CustomersBalanceSummary); diff --git a/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummaryActionsBar.js b/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummaryActionsBar.js new file mode 100644 index 000000000..d70519dd5 --- /dev/null +++ b/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummaryActionsBar.js @@ -0,0 +1,133 @@ +import React from 'react'; +import { + NavbarGroup, + Button, + Classes, + NavbarDivider, + Popover, + PopoverInteractionKind, + Position, +} from '@blueprintjs/core'; +import { FormattedMessage as T } from 'react-intl'; +import classNames from 'classnames'; + +import Icon from 'components/Icon'; +import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'; +import NumberFormatDropdown from 'components/NumberFormatDropdown'; + +import withCustomersBalanceSummary from './withCustomersBalanceSummary'; +import withCustomersBalanceSummaryActions from './withCustomersBalanceSummaryActions'; +import { useCustomersBalanceSummaryContext } from './CustomersBalanceSummaryProvider'; +import { compose, saveInvoke } from 'utils'; + +/** + * customer balance summary action bar. + */ +function CustomersBalanceSummaryActionsBar({ + // #ownProps + numberFormat, + onNumberFormatSubmit, + + //#withCustomersBalanceSummary + isFilterDrawerOpen, + + //#withCustomersBalanceSummaryActions + toggleCustomerBalanceFilterDrawer, +}) { + const { + refetch, + isCustomersBalanceLoading, + } = useCustomersBalanceSummaryContext(); + + // handle filter toggle click. + const handleFilterToggleClick = () => { + toggleCustomerBalanceFilterDrawer(); + }; + + // Handle recalculate the report button. + const handleRecalcReport = () => { + refetch(); + }; + + // Handle number format form submit. + const handleNumberFormatSubmit = (values) => { + saveInvoke(onNumberFormatSubmit, values); + }; + + return ( + + + + + + + + + ); +} + +export default compose( + withCustomersBalanceSummary(({ customersBalanceDrawerFilter }) => ({ + customersBalanceDrawerFilter, + })), + withCustomersBalanceSummaryActions, +)(CustomersBalanceSummaryHeader); diff --git a/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummaryProvider.js b/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummaryProvider.js new file mode 100644 index 000000000..8ab7f48ab --- /dev/null +++ b/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummaryProvider.js @@ -0,0 +1,42 @@ +import React, { createContext, useContext } from 'react'; +import FinancialReportPage from '../FinancialReportPage'; +import { useCustomerBalanceSummaryReport } from 'hooks/query'; +import { transformFilterFormToQuery } from '../common'; + +const CustomersBalanceSummaryContext = createContext(); + +/** + * Customers balance summary provider. + */ +function CustomersBalanceSummaryProvider({ filter, ...props }) { + + // const query = React.useMemo(() => transformFilterFormToQuery(filter), [ + // filter, + // ]); + + const { + data: CustomerBalanceSummary, + isLoading: isCustomersBalanceLoading, + isFetching: isCustomersBalanceFetching, + refetch + } = useCustomerBalanceSummaryReport(filter, { + keepPreviousData: true, + }); + + const provider = { + CustomerBalanceSummary, + isCustomersBalanceFetching, + isCustomersBalanceLoading, + refetch, + }; + return ( + + + + ); +} + +const useCustomersBalanceSummaryContext = () => + useContext(CustomersBalanceSummaryContext); + +export { CustomersBalanceSummaryProvider, useCustomersBalanceSummaryContext }; diff --git a/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummaryTable.js b/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummaryTable.js new file mode 100644 index 000000000..57ee2655f --- /dev/null +++ b/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummaryTable.js @@ -0,0 +1,48 @@ +import React, { useMemo, useCallback } from 'react'; +import { useIntl } from 'react-intl'; +import classNames from 'classnames'; + +import FinancialSheet from 'components/FinancialSheet'; +import DataTable from 'components/DataTable'; + +import { useCustomersBalanceSummaryContext } from './CustomersBalanceSummaryProvider'; +import { useCustomersSummaryColumns } from './components'; + +/** + * customers balance summary table. + */ +export default function CustomersBalanceSummaryTable({ + // #ownProps + companyName, +}) { + const { formatMessage } = useIntl(); + + const { + isCustomersBalanceLoading, + CustomerBalanceSummary: { tableRows }, + } = useCustomersBalanceSummaryContext(); + + const columns = useCustomersSummaryColumns(); + + const rowClassNames = (row) => { + return [`row-type--${row.original.rowTypes}`]; + }; + + return ( + + + + ); +} diff --git a/client/src/containers/FinancialStatements/CustomersBalanceSummary/components.js b/client/src/containers/FinancialStatements/CustomersBalanceSummary/components.js new file mode 100644 index 000000000..0ac61d5c9 --- /dev/null +++ b/client/src/containers/FinancialStatements/CustomersBalanceSummary/components.js @@ -0,0 +1,48 @@ +import React from 'react'; +import { formatMessage } from 'services/intl'; + +import { If } from 'components'; +import FinancialLoadingBar from '../FinancialLoadingBar'; +import { useCustomersBalanceSummaryContext } from './CustomersBalanceSummaryProvider'; + +/** + * Retrieve customers balance summary columns. + */ +export const useCustomersSummaryColumns = () => { + return React.useMemo( + () => [ + { + Header: formatMessage({ id: 'customer_name' }), + accessor: 'cells[0].value', + className: 'customer_name', + width: 240, + }, + { + 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, + }, + ], + [formatMessage], + ); +}; + +/** + * customers balance summary loading bar. + */ +export function CustomersBalanceLoadingBar() { + const { isCustomersBalanceFetching } = useCustomersBalanceSummaryContext(); + + return ( + + + + ); +} diff --git a/client/src/containers/FinancialStatements/CustomersBalanceSummary/withCustomersBalanceSummary.js b/client/src/containers/FinancialStatements/CustomersBalanceSummary/withCustomersBalanceSummary.js new file mode 100644 index 000000000..80cf7382a --- /dev/null +++ b/client/src/containers/FinancialStatements/CustomersBalanceSummary/withCustomersBalanceSummary.js @@ -0,0 +1,15 @@ +import { connect } from 'react-redux'; +import { getCustomersBalanceSummaryFilterDrawer } from 'store/financialStatement/financialStatements.selectors'; + +export default (mapState) => { + const mapStateToProps = (state, props) => { + const mapped = { + customersBalanceDrawerFilter: getCustomersBalanceSummaryFilterDrawer( + state, + props, + ), + }; + return mapState ? mapState(mapped, state, props) : mapped; + }; + return connect(mapStateToProps); +}; diff --git a/client/src/containers/FinancialStatements/CustomersBalanceSummary/withCustomersBalanceSummaryActions.js b/client/src/containers/FinancialStatements/CustomersBalanceSummary/withCustomersBalanceSummaryActions.js new file mode 100644 index 000000000..b7679f2b1 --- /dev/null +++ b/client/src/containers/FinancialStatements/CustomersBalanceSummary/withCustomersBalanceSummaryActions.js @@ -0,0 +1,9 @@ +import { connect } from 'react-redux'; +import { toggleCustomersBalanceSummaryFilterDrawer } from 'store/financialStatement/financialStatements.actions'; + +const mapActionsToProps = (dispatch) => ({ + toggleCustomerBalanceFilterDrawer: (toggle) => + dispatch(toggleCustomersBalanceSummaryFilterDrawer(toggle)), +}); + +export default connect(null, mapActionsToProps); 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 ( + + + + + + + + + ); +} + +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); diff --git a/client/src/hooks/query/financialReports.js b/client/src/hooks/query/financialReports.js index 1ec454c3b..3096bfc7a 100644 --- a/client/src/hooks/query/financialReports.js +++ b/client/src/hooks/query/financialReports.js @@ -287,3 +287,80 @@ export function useSalesByItems(query, props) { }, ); } + +/** + * Retrieve customers balance summary report. + */ +export function useCustomerBalanceSummaryReport(query, props) { + return useRequestQuery( + [t.FINANCIAL_REPORT, t.CUSTOMERS_BALANCE_SUMMARY, query], + { + method: 'get', + url: '/financial_statements/customer-balance-summary', + params: query, + }, + { + select: (res) => ({ + columns: res.data.columns, + query: res.data.query, + tableRows: res.data.table.rows, + }), + defaultData: { + tableRows: [], + query: {}, + }, + ...props, + }, + ); +} + +/** + * Retrieve vendors balance summary report. + */ +export function useVendorsBalanceSummaryReport(query, props) { + return useRequestQuery( + [t.FINANCIAL_REPORT, t.VENDORS_BALANCE_SUMMARY, query], + { + method: 'get', + url: '/financial_statements/vendor-balance-summary', + params: query, + }, + { + select: (res) => ({ + columns: res.data.columns, + query: res.data.query, + tableRows: res.data.table.rows, + }), + defaultData: { + tableRows: [], + query: {}, + }, + ...props, + }, + ); +} + +/** + * Retrieve customers transcations report. + */ +export function useCustomersTranscationsReport(query, props) { + return useRequestQuery( + [t.FINANCIAL_REPORT, t.CUSTOMERS_TRANSACTIONS, query], + { + method: 'get', + url: '/financial_statements/transactions-by-customers', + params: query, + }, + { + select: (res) => ({ + data: res.data.table, + tableRows: res.data.table.rows, + }), + defaultData: { + tableRows: [], + data: [], + }, + ...props, + }, + ); +} diff --git a/client/src/routes/dashboard.js b/client/src/routes/dashboard.js index 52a816b2e..1c9fa0e68 100644 --- a/client/src/routes/dashboard.js +++ b/client/src/routes/dashboard.js @@ -112,7 +112,8 @@ export default [ import('containers/FinancialStatements/GeneralLedger/GeneralLedger'), ), breadcrumb: 'General Ledger', - hint: "Reports every transaction going in and out of your accounts and organized by accounts and date to monitoring activity of accounts.", + hint: + 'Reports every transaction going in and out of your accounts and organized by accounts and date to monitoring activity of accounts.', hotkey: 'shift+4', pageTitle: formatMessage({ id: 'general_ledger' }), backLink: true, @@ -124,7 +125,8 @@ export default [ import('containers/FinancialStatements/BalanceSheet/BalanceSheet'), ), breadcrumb: 'Balance Sheet', - hint: "Reports a company's assets, liabilities and shareholders' equity at a specific point in time with comparison period(s).", + hint: + "Reports a company's assets, liabilities and shareholders' equity at a specific point in time with comparison period(s).", hotkey: 'shift+1', pageTitle: formatMessage({ id: 'balance_sheet' }), backLink: true, @@ -138,7 +140,8 @@ export default [ ), ), breadcrumb: 'Trial Balance Sheet', - hint: "Summarizes the credit and debit balance of each account in your chart of accounts at a specific point in time. ", + hint: + 'Summarizes the credit and debit balance of each account in your chart of accounts at a specific point in time. ', hotkey: 'shift+5', pageTitle: formatMessage({ id: 'trial_balance_sheet' }), backLink: true, @@ -150,7 +153,8 @@ export default [ import('containers/FinancialStatements/ProfitLossSheet/ProfitLossSheet'), ), breadcrumb: 'Profit Loss Sheet', - hint: "Reports the revenues, costs and expenses incurred during a specific point in time with comparison period(s).", + hint: + 'Reports the revenues, costs and expenses incurred during a specific point in time with comparison period(s).', hotkey: 'shift+2', pageTitle: formatMessage({ id: 'profit_loss_sheet' }), backLink: true, @@ -162,7 +166,8 @@ export default [ import('containers/FinancialStatements/ARAgingSummary/ARAgingSummary'), ), breadcrumb: 'Receivable Aging Summary', - hint: "Summarize total unpaid balances of customers invoices with number of days the unpaid invoice is overdue.", + hint: + 'Summarize total unpaid balances of customers invoices with number of days the unpaid invoice is overdue.', pageTitle: formatMessage({ id: 'receivable_aging_summary' }), backLink: true, sidebarExpand: false, @@ -173,7 +178,8 @@ export default [ import('containers/FinancialStatements/APAgingSummary/APAgingSummary'), ), breadcrumb: 'Payable Aging Summary', - hint: "Summarize total unpaid balances of vendors purchase invoices with the number of days the unpaid invoice is overdue.", + hint: + 'Summarize total unpaid balances of vendors purchase invoices with the number of days the unpaid invoice is overdue.', pageTitle: formatMessage({ id: 'payable_aging_summary' }), backLink: true, sidebarExpand: false, @@ -184,7 +190,8 @@ export default [ import('containers/FinancialStatements/Journal/Journal'), ), breadcrumb: 'Journal Sheet', - hint: "The debit and credit entries of system transactions, sorted by date.", + hint: + 'The debit and credit entries of system transactions, sorted by date.', hotkey: 'shift+3', pageTitle: formatMessage({ id: 'journal_sheet' }), sidebarExpand: false, @@ -210,7 +217,8 @@ export default [ ), breadcrumb: 'Sales by Items', pageTitle: formatMessage({ id: 'sales_by_items' }), - hint: 'Summarize the business’s sold items quantity, income and average income rate of each item during a specific point in time.', + hint: + 'Summarize the business’s sold items quantity, income and average income rate of each item during a specific point in time.', backLink: true, sidebarExpand: false, }, @@ -222,11 +230,64 @@ export default [ ), ), breadcrumb: 'Inventory Valuation ', - hint: 'Summerize your transactions for each inventory item and how they affect quantity, valuation and weighted average.', + hint: + 'Summerize your transactions for each inventory item and how they affect quantity, valuation and weighted average.', pageTitle: formatMessage({ id: 'inventory_valuation' }), backLink: true, sidebarExpand: false, }, + { + path: `/financial-reports/customers-balance-summary`, + component: lazy(() => + import( + 'containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummary' + ), + ), + breadcrumb: 'Customers Balance Summary ', + hint: '..', + pageTitle: formatMessage({ id: 'customers_balance_summary' }), + backLink: true, + sidebarExpand: false, + }, + { + path: `/financial-reports/vendors-balance-summary`, + component: lazy(() => + import( + 'containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummary' + ), + ), + breadcrumb: 'Vendors Balance Summary ', + hint: '..', + pageTitle: formatMessage({ id: 'vendors_balance_summary' }), + backLink: true, + sidebarExpand: false, + }, + { + path: `/financial-reports/transactions-by-customers`, + // component: lazy(() => + // import( + // 'containers/FinancialStatements/CustomersTransactions/CustomersTransactions' + // ), + // ), + breadcrumb: 'Customers Transactions ', + hint: '..', + pageTitle: formatMessage({ id: 'customers_transactions' }), + backLink: true, + sidebarExpand: false, + }, + { + path: `/financial-reports/vendors-transactions`, + // component: lazy(() => + // import( + // 'containers/FinancialStatements/' + // ), + // ), + breadcrumb: 'Vendors Transactions ', + hint: '..', + pageTitle: formatMessage({ id: 'vendors_transactions' }), + backLink: true, + sidebarExpand: false, + }, { path: '/financial-reports', component: lazy(() => diff --git a/client/src/store/financialStatement/financialStatements.actions.js b/client/src/store/financialStatement/financialStatements.actions.js index eaca5f0f4..86f2e9506 100644 --- a/client/src/store/financialStatement/financialStatements.actions.js +++ b/client/src/store/financialStatement/financialStatements.actions.js @@ -128,3 +128,54 @@ export function toggleInventoryValuationFilterDrawer(toggle) { }, }; } + + +/** + * Toggles display of the customers balance summary filter drawer. + * @param {boolean} toggle + */ + export function toggleCustomersBalanceSummaryFilterDrawer(toggle) { + return { + type: `${t.CUSTOMERS_BALANCE_SUMMARY}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`, + payload: { + toggle, + }, + }; +} +/** + * Toggles display of the vendors balance summary filter drawer. + * @param {boolean} toggle + */ + export function toggleVendorsBalanceSummaryFilterDrawer(toggle) { + return { + type: `${t.VENDORS_BALANCE_SUMMARY}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`, + payload: { + toggle, + }, + }; +} +/** + * Toggles display of the customers transactions filter drawer. + * @param {boolean} toggle + */ + export function toggleCustomersTransactionsFilterDrawer(toggle) { + return { + type: `${t.CUSTOMERS_TRANSACTIONS}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`, + payload: { + toggle, + }, + }; +} + +// /** +// * Toggles display of the vendors transactions filter drawer. +// * @param {boolean} toggle +// */ +// export function toggleVendorsTransactionsFilterDrawer(toggle) { +// return { +// type: `${t.VENDORS_TRANSACTIONS}/${t.DISPLAY_FILTER_DRAWER_TOGGLE}`, +// payload: { +// toggle, +// }, +// }; +// } \ No newline at end of file diff --git a/client/src/store/financialStatement/financialStatements.reducer.js b/client/src/store/financialStatement/financialStatements.reducer.js index d21666577..e0b725b33 100644 --- a/client/src/store/financialStatement/financialStatements.reducer.js +++ b/client/src/store/financialStatement/financialStatements.reducer.js @@ -33,6 +33,18 @@ const initialState = { inventoryValuation: { displayFilterDrawer: false, }, + customersBalanceSummary: { + displayFilterDrawer: false, + }, + vendorsBalanceSummary: { + displayFilterDrawer: false, + }, + customersTransactions: { + displayFilterDrawer: false, + }, + vendorsTransactions: { + displayFilterDrawer: false, + }, }; /** @@ -63,4 +75,20 @@ export default createReducer(initialState, { t.INVENTORY_VALUATION, 'inventoryValuation', ), + ...financialStatementFilterToggle( + t.CUSTOMERS_BALANCE_SUMMARY, + 'customersBalanceSummary', + ), + ...financialStatementFilterToggle( + t.VENDORS_BALANCE_SUMMARY, + 'vendorsBalanceSummary', + ), + ...financialStatementFilterToggle( + t.CUSTOMERS_TRANSACTIONS, + 'customersTransactions', + ), + ...financialStatementFilterToggle( + t.VENDORS_TRANSACTIONS, + 'vendorsTransactions', + ), }); diff --git a/client/src/store/financialStatement/financialStatements.selectors.js b/client/src/store/financialStatement/financialStatements.selectors.js index 92a9f7410..c87c10cc9 100644 --- a/client/src/store/financialStatement/financialStatements.selectors.js +++ b/client/src/store/financialStatement/financialStatements.selectors.js @@ -49,6 +49,22 @@ export const inventoryValuationFilterDrawerSelector = (state) => { return filterDrawerByTypeSelector('inventoryValuation')(state); }; +export const customerBalanceSummaryFilterDrawerSelector = (state) => { + return filterDrawerByTypeSelector('customersBalanceSummary')(state); +}; + +export const vendorsBalanceSummaryFilterDrawerSelector = (state) => { + return filterDrawerByTypeSelector('vendorsBalanceSummary')(state); +}; + +export const customersTransactionsFilterDrawerSelector = (state) => { + return filterDrawerByTypeSelector('customersTransactions')(state); +}; + +export const vendorsTransactionsFilterDrawerSelector = (state) => { + return filterDrawerByTypeSelector('vendorsTransactions')(state); +}; + /** * Retrieve balance sheet filter drawer. */ @@ -156,3 +172,42 @@ export const getInventoryValuationFilterDrawer = createSelector( return isOpen; }, ); + +/** + * Retrieve customers balance summary filter drawer. + */ +export const getCustomersBalanceSummaryFilterDrawer = createSelector( + customerBalanceSummaryFilterDrawerSelector, + (isOpen) => { + return isOpen; + }, +); +/** + * Retrieve vendors balance summary filter drawer. + */ +export const getVendorsBalanceSummaryFilterDrawer = createSelector( + vendorsBalanceSummaryFilterDrawerSelector, + (isOpen) => { + return isOpen; + }, +); + +/** + * Retrieve customers transactions filter drawer. + */ +export const getCustomersTransactionsFilterDrawer = createSelector( + customersTransactionsFilterDrawerSelector, + (isOpen) => { + return isOpen; + }, +); + +/** + * Retrieve vendors transactions filter drawer. + */ +export const getVendorsTransactionsFilterDrawer = createSelector( + vendorsTransactionsFilterDrawerSelector, + (isOpen) => { + return isOpen; + }, +); diff --git a/client/src/store/financialStatement/financialStatements.types.js b/client/src/store/financialStatement/financialStatements.types.js index f5fabee35..f80a94ada 100644 --- a/client/src/store/financialStatement/financialStatements.types.js +++ b/client/src/store/financialStatement/financialStatements.types.js @@ -10,4 +10,8 @@ export default { PURCHASES_BY_ITEMS: 'PURCHASES_BY_ITEMS', SALES_BY_ITEMS: 'SALES_BY_ITEMS', INVENTORY_VALUATION: 'INVENTORY_VALUATION', + CUSTOMERS_BALANCE_SUMMARY: 'CUSTOMERS BALANCE SUMMARY', + VENDORS_BALANCE_SUMMARY: 'VENDORS BALANCE SUMMARY', + CUSTOMERS_TRANSACTIONS: 'CUSTOMERS TRANSACTIONS', + // VENDORS_TRANSACTIONS: 'CUSTOMERS TRANSACTIONS', }; diff --git a/client/src/style/pages/FinancialStatements/ContactsBalanceSummary.scss b/client/src/style/pages/FinancialStatements/ContactsBalanceSummary.scss new file mode 100644 index 000000000..107c2b340 --- /dev/null +++ b/client/src/style/pages/FinancialStatements/ContactsBalanceSummary.scss @@ -0,0 +1,31 @@ +.financial-sheet { + &--customers-balance-summary, + &--vendors-balance-summary { + .financial-sheet__table { + .thead, + .tbody { + .tr .td.customer_name ~ .td, + .tr .th.customer_name ~ .th { + text-align: right; + } + } + .tbody { + .tr:not(.no-results) { + .td { + border-bottom: 0; + padding-top: 0.4rem; + padding-bottom: 0.4rem; + } + &.row-type--TOTAL { + font-weight: 500; + + .td { + border-top: 1px solid #bbb; + border-bottom: 3px double #333; + } + } + } + } + } + } +}