From c361a5852c88096f789b30b71b2f2c02fc85e668 Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Wed, 9 Feb 2022 19:50:49 +0200 Subject: [PATCH] fix(BS|PL): report query. --- .../BalanceSheet/BalanceSheet.js | 27 +- .../BalanceSheet/BalanceSheetHeader.js | 1 - .../BalanceSheetHeaderComparisonPanal.js | 67 +-- .../BalanceSheet/BalanceSheetProvider.js | 1 + .../BalanceSheet/components.js | 3 +- .../BalanceSheet/dynamicColumns.js | 333 ++++++++++++ .../FinancialStatements/BalanceSheet/utils.js | 462 +++++----------- .../ProfitLossSheet/ProfitLossProvider.js | 4 + .../ProfitLossSheet/ProfitLossSheet.js | 34 +- .../ProfitLossSheet/ProfitLossSheetHeader.js | 27 +- .../ProfitLossSheetHeaderComparisonPanel.js | 67 +-- .../ProfitLossSheet/dynamicColumns.js | 383 ++++++++++++++ .../ProfitLossSheet/hooks.js | 7 +- .../ProfitLossSheet/utils.js | 496 +++++------------- src/hooks/index.js | 27 + 15 files changed, 1090 insertions(+), 849 deletions(-) create mode 100644 src/containers/FinancialStatements/BalanceSheet/dynamicColumns.js create mode 100644 src/containers/FinancialStatements/ProfitLossSheet/dynamicColumns.js diff --git a/src/containers/FinancialStatements/BalanceSheet/BalanceSheet.js b/src/containers/FinancialStatements/BalanceSheet/BalanceSheet.js index 7395a11b1..5e56f135d 100644 --- a/src/containers/FinancialStatements/BalanceSheet/BalanceSheet.js +++ b/src/containers/FinancialStatements/BalanceSheet/BalanceSheet.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import moment from 'moment'; import { BalanceSheetAlerts, BalanceSheetLoadingBar } from './components'; @@ -12,6 +12,7 @@ import { BalanceSheetBody } from './BalanceSheetBody'; import withBalanceSheetActions from './withBalanceSheetActions'; +import { useBalanceSheetQuery } from './utils'; import { compose } from 'utils'; /** @@ -22,26 +23,22 @@ function BalanceSheet({ // #withBalanceSheetActions toggleBalanceSheetFilterDrawer, }) { - const [filter, setFilter] = useState({ - fromDate: moment().startOf('year').format('YYYY-MM-DD'), - toDate: moment().endOf('year').format('YYYY-MM-DD'), - basis: 'cash', - displayColumnsType: 'total', - filterByOption: 'without-zero-balance', - }); + // Balance sheet query. + const { query, setLocationQuery } = useBalanceSheetQuery(); + // Handle re-fetch balance sheet after filter change. const handleFilterSubmit = (filter) => { - const _filter = { + const newFilter = { ...filter, fromDate: moment(filter.fromDate).format('YYYY-MM-DD'), toDate: moment(filter.toDate).format('YYYY-MM-DD'), }; - setFilter({ ..._filter }); + setLocationQuery({ ...newFilter }); }; // Handle number format submit. const handleNumberFormatSubmit = (values) => { - setFilter({ - ...filter, + setLocationQuery({ + ...query, numberFormat: values, }); }; @@ -54,9 +51,9 @@ function BalanceSheet({ ); return ( - + @@ -65,7 +62,7 @@ function BalanceSheet({ diff --git a/src/containers/FinancialStatements/BalanceSheet/BalanceSheetHeader.js b/src/containers/FinancialStatements/BalanceSheet/BalanceSheetHeader.js index 0a2c51006..78ec8ddca 100644 --- a/src/containers/FinancialStatements/BalanceSheet/BalanceSheetHeader.js +++ b/src/containers/FinancialStatements/BalanceSheet/BalanceSheetHeader.js @@ -43,7 +43,6 @@ function BalanceSheetHeader({ }, defaultValues, ); - // Validation schema. const validationSchema = getBalanceSheetHeaderValidationSchema(); diff --git a/src/containers/FinancialStatements/BalanceSheet/BalanceSheetHeaderComparisonPanal.js b/src/containers/FinancialStatements/BalanceSheet/BalanceSheetHeaderComparisonPanal.js index d2da96772..e2ce7150f 100644 --- a/src/containers/FinancialStatements/BalanceSheet/BalanceSheetHeaderComparisonPanal.js +++ b/src/containers/FinancialStatements/BalanceSheet/BalanceSheetHeaderComparisonPanal.js @@ -4,12 +4,14 @@ import { FastField, Field } from 'formik'; import { FormGroup, Checkbox } from '@blueprintjs/core'; import styled from 'styled-components'; -import { FormattedMessage as T } from 'components'; - -import { Row, Col, FieldHint } from '../../../components'; +import { Row, Col, FieldHint, FormattedMessage as T } from 'components'; import { handlePreviousYearCheckBoxChange, + handlePreviousYearChangeCheckboxChange, handlePreviousPeriodCheckBoxChange, + handlePreivousPeriodPercentageCheckboxChange, + handlePreviousYearPercentageCheckboxChange, + handlePreviousPeriodChangeCheckboxChange, } from './utils'; /** @@ -19,7 +21,7 @@ export default function BalanceSheetHeaderComparisonPanal() { return ( {/**----------- Previous Year -----------*/} - + {({ form, field }) => ( }> - - {({ form: { setFieldValue }, field }) => ( + + {({ form, field }) => ( }> } {...field} - onChange={({ currentTarget }) => { - setFieldValue('previous_year', currentTarget.checked); - setFieldValue( - 'previous_year_amount_change', - currentTarget.checked, - ); - }} + onChange={handlePreviousYearChangeCheckboxChange(form)} /> )} - - {({ form: { setFieldValue }, field }) => ( + + {({ form, field }) => ( }> } {...field} - onChange={({ currentTarget }) => { - setFieldValue('previous_year', currentTarget.checked); - setFieldValue( - 'previous_year_percentage_change', - currentTarget.checked, - ); - }} + onChange={handlePreviousYearPercentageCheckboxChange(form)} /> )} @@ -75,7 +65,7 @@ export default function BalanceSheetHeaderComparisonPanal() { {/*------------ Previous Period -----------*/} - + {({ form, field }) => ( }> - - {({ form: { setFieldValue }, field }) => ( + + {({ form, field }) => ( }> } {...field} - onChange={({ currentTarget }) => { - setFieldValue('previous_period', currentTarget.checked); - setFieldValue( - 'previous_period_amount_change', - currentTarget.checked, - ); - }} + onChange={handlePreviousPeriodChangeCheckboxChange(form)} /> )} - - {({ form: { setFieldValue }, field }) => ( + + {({ form, field }) => ( }> } {...field} - onChange={({ currentTarget }) => { - setFieldValue('previous_period', currentTarget.checked); - setFieldValue( - 'previous_period_percentage_change', - currentTarget.checked, - ); - }} + onChange={handlePreivousPeriodPercentageCheckboxChange(form)} /> )} @@ -136,7 +111,7 @@ export default function BalanceSheetHeaderComparisonPanal() { {/**----------- % of Column -----------*/} - + {({ field }) => ( }> {/**----------- % of Row -----------*/} - + {({ field }) => ( }> `cells[${index}].value`; + +const getReportColWidth = (data, accessor, headerText) => { + return getColumnWidth( + data, + accessor, + { magicSpacing: 10, minWidth: 100 }, + headerText, + ); +}; + + +/** + * Account name column mapper. + */ +const accountNameMapper = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + key: column.key, + Header: column.label, + accessor, + className: column.key, + textOverview: true, + width: Math.max(width, 300), + sticky: Align.Left, + }; +}); + +/** + * Assoc columns to total column. + */ +const assocColumnsToTotalColumn = R.curry((data, column, columnAccessor) => { + const columns = totalColumnsComposer(data, column); + + return R.assoc('columns', columns, columnAccessor); +}); + +/** + * Detarmines whether the given column has children columns. + * @returns {boolean} + */ +const isColumnHasColumns = (column) => !isEmpty(column.children); + +/** + * + * @param {*} data + * @param {*} column + * @returns + */ +const dateRangeSoloColumnAttrs = (data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + + return { + accessor, + width: getReportColWidth(data, accessor), + }; +}; + +/** + * Date range columns mapper. + */ +const dateRangeMapper = R.curry((data, column) => { + const isDateColumnHasColumns = isColumnHasColumns(column); + + const columnAccessor = { + Header: column.label, + key: column.key, + disableSortBy: true, + textOverview: true, + align: isDateColumnHasColumns ? Align.Center : Align.Right, + }; + return R.compose( + R.when( + R.always(isDateColumnHasColumns), + assocColumnsToTotalColumn(data, column), + ), + R.when( + R.always(!isDateColumnHasColumns), + R.mergeLeft(dateRangeSoloColumnAttrs(data, column)), + ), + )(columnAccessor); +}); + +/** + * Total column mapper. + */ +const totalMapper = R.curry((data, column) => { + const hasChildren = !isEmpty(column.children); + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + const columnAccessor = { + key: column.key, + Header: column.label, + accessor, + textOverview: true, + Cell: CellTextSpan, + width, + disableSortBy: true, + align: hasChildren ? Align.Center : Align.Right, + }; + return R.compose( + R.when(R.always(hasChildren), assocColumnsToTotalColumn(data, column)), + )(columnAccessor); +}); + +/** + * `Percentage of column` column accessor. + */ +const percentageOfColumnAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * `Percentage of row` column accessor. + */ +const percentageOfRowAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * Previous year column accessor. + */ +const previousYearAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * Pervious year change column accessor. + */ +const previousYearChangeAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * Previous year percentage column accessor. + */ +const previousYearPercentageAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * Previous period column accessor. + */ +const previousPeriodAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * Previous period change column accessor. + */ +const previousPeriodChangeAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * Previous period percentage column accessor. + */ +const previousPeriodPercentageAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * + * @param {*} column + * @param {*} index + * @returns + */ +const totalColumnsMapper = R.curry((data, column) => { + return R.compose( + R.when(R.pathEq(['key'], 'total'), totalMapper(data)), + // Percetage of column/row. + R.when( + R.pathEq(['key'], 'percentage_of_column'), + percentageOfColumnAccessor(data), + ), + R.when( + R.pathEq(['key'], 'percentage_of_row'), + percentageOfRowAccessor(data), + ), + // Previous year. + R.when(R.pathEq(['key'], 'previous_year'), previousYearAccessor(data)), + R.when( + R.pathEq(['key'], 'previous_year_change'), + previousYearChangeAccessor(data), + ), + R.when( + R.pathEq(['key'], 'previous_year_percentage'), + previousYearPercentageAccessor(data), + ), + // Pervious period. + R.when(R.pathEq(['key'], 'previous_period'), previousPeriodAccessor(data)), + R.when( + R.pathEq(['key'], 'previous_period_change'), + previousPeriodChangeAccessor(data), + ), + R.when( + R.pathEq(['key'], 'previous_period_percentage'), + previousPeriodPercentageAccessor(data), + ), + )(column); +}); + +/** + * Total sub-columns composer. + */ +const totalColumnsComposer = R.curry((data, column) => { + return R.map(totalColumnsMapper(data), column.children); +}); + +/** + * Detarmines the given string starts with `date-range` string. + */ +const isMatchesDateRange = (r) => R.match(/^date-range/g, r).length > 0; + +/** + * Dynamic column mapper. + */ +const dynamicColumnMapper = R.curry((data, column) => { + const indexTotalMapper = totalMapper(data); + const indexAccountNameMapper = accountNameMapper(data); + const indexDatePeriodMapper = dateRangeMapper(data); + + return R.compose( + R.when(R.pathSatisfies(isMatchesDateRange, ['key']), indexDatePeriodMapper), + R.when(R.pathEq(['key'], 'name'), indexAccountNameMapper), + R.when(R.pathEq(['key'], 'total'), indexTotalMapper), + )(column); +}); + +/** + * Cash flow dynamic columns. + */ +export const dynamicColumns = (columns, data) => { + return R.map(dynamicColumnMapper(data), columns); +}; diff --git a/src/containers/FinancialStatements/BalanceSheet/utils.js b/src/containers/FinancialStatements/BalanceSheet/utils.js index b75d5f621..ba14968ca 100644 --- a/src/containers/FinancialStatements/BalanceSheet/utils.js +++ b/src/containers/FinancialStatements/BalanceSheet/utils.js @@ -1,27 +1,65 @@ -import * as Yup from 'yup'; +import React from 'react'; import * as R from 'ramda'; -import { isEmpty } from 'lodash'; -import intl from 'react-intl-universal'; import moment from 'moment'; +import * as Yup from 'yup'; +import intl from 'react-intl-universal'; -import { Align } from 'common'; -import { CellTextSpan } from 'components/Datatable/Cells'; -import { getColumnWidth } from 'utils'; +import { transformToForm } from 'utils'; +import { useLocationQuery, useMutateLocationQuery } from 'hooks'; -const getTableCellValueAccessor = (index) => `cells[${index}].value`; +/** + * Retrieves the default balance sheet query. + * @returns {} + */ +export const getDefaultBalanceSheetQuery = () => ({ + fromDate: moment().startOf('year').format('YYYY-MM-DD'), + toDate: moment().endOf('year').format('YYYY-MM-DD'), + basis: 'cash', + displayColumnsType: 'total', + filterByOption: 'without-zero-balance', -const getReportColWidth = (data, accessor, headerText) => { - return getColumnWidth( - data, - accessor, - { magicSpacing: 10, minWidth: 100 }, - headerText, - ); + previousYear: false, + previousYearAmountChange: false, + previousYearPercentageChange: false, + + previousPeriod: false, + previousPeriodAmountChange: false, + previousPeriodPercentageChange: false, + + // Percentage columns. + percentageColumn: false, + percentageRow: false, +}); + +/** + * Retrieves the balance sheet query. + */ +export const useBalanceSheetQuery = () => { + // Retrieves location query. + const locationQuery = useLocationQuery(); + + // Mutates the location query. + const { mutate: setLocationQuery } = useMutateLocationQuery(); + + // Merges the default filter query with location URL query. + const query = React.useMemo(() => { + const defaultQuery = getDefaultBalanceSheetQuery(); + + return { + ...defaultQuery, + ...transformToForm(Object.fromEntries([...locationQuery]), defaultQuery), + }; + }, [locationQuery]); + + return { + query, + locationQuery, + setLocationQuery, + }; }; /** - * - * @returns + * Retrieves the balance sheet header default values. */ export const getBalanceSheetHeaderDefaultValues = () => { return { @@ -34,8 +72,7 @@ export const getBalanceSheetHeaderDefaultValues = () => { }; /** - * - * @returns + * Retrieves the balance sheet header validation schema. */ export const getBalanceSheetHeaderValidationSchema = () => Yup.object().shape({ @@ -50,330 +87,81 @@ export const getBalanceSheetHeaderValidationSchema = () => }); /** - * Account name column mapper. + * Handles previous year checkbox change. */ -const accountNameMapper = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - key: column.key, - Header: column.label, - accessor, - className: column.key, - textOverview: true, - width: Math.max(width, 300), - sticky: Align.Left, - }; -}); - -/** - * Assoc columns to total column. - */ -const assocColumnsToTotalColumn = R.curry((data, column, columnAccessor) => { - const columns = totalColumnsComposer(data, column); - - return R.assoc('columns', columns, columnAccessor); -}); - -/** - * Detarmines whether the given column has children columns. - * @returns {boolean} - */ -const isColumnHasColumns = (column) => !isEmpty(column.children); - -/** - * - * @param {*} data - * @param {*} column - * @returns - */ -const dateRangeSoloColumnAttrs = (data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - - return { - accessor, - width: getReportColWidth(data, accessor), - }; -}; - -/** - * Date range columns mapper. - */ -const dateRangeMapper = R.curry((data, column) => { - const isDateColumnHasColumns = isColumnHasColumns(column); - - const columnAccessor = { - Header: column.label, - key: column.key, - disableSortBy: true, - textOverview: true, - align: isDateColumnHasColumns ? Align.Center : Align.Right, - }; - return R.compose( - R.when( - R.always(isDateColumnHasColumns), - assocColumnsToTotalColumn(data, column), - ), - R.when( - R.always(!isDateColumnHasColumns), - R.mergeLeft(dateRangeSoloColumnAttrs(data, column)), - ), - )(columnAccessor); -}); - -/** - * Total column mapper. - */ -const totalMapper = R.curry((data, column) => { - const hasChildren = !isEmpty(column.children); - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - const columnAccessor = { - key: column.key, - Header: column.label, - accessor, - textOverview: true, - Cell: CellTextSpan, - width, - disableSortBy: true, - align: hasChildren ? Align.Center : Align.Right, - }; - return R.compose( - R.when(R.always(hasChildren), assocColumnsToTotalColumn(data, column)), - )(columnAccessor); -}); - -/** - * `Percentage of column` column accessor. - */ -const percentageOfColumnAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * `Percentage of row` column accessor. - */ -const percentageOfRowAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * Previous year column accessor. - */ -const previousYearAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * Pervious year change column accessor. - */ -const previousYearChangeAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * Previous year percentage column accessor. - */ -const previousYearPercentageAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * Previous period column accessor. - */ -const previousPeriodAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * Previous period change column accessor. - */ -const previousPeriodChangeAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * Previous period percentage column accessor. - */ -const previousPeriodPercentageAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * - * @param {*} column - * @param {*} index - * @returns - */ -const totalColumnsMapper = R.curry((data, column) => { - return R.compose( - R.when(R.pathEq(['key'], 'total'), totalMapper(data)), - // Percetage of column/row. - R.when( - R.pathEq(['key'], 'percentage_of_column'), - percentageOfColumnAccessor(data), - ), - R.when( - R.pathEq(['key'], 'percentage_of_row'), - percentageOfRowAccessor(data), - ), - // Previous year. - R.when(R.pathEq(['key'], 'previous_year'), previousYearAccessor(data)), - R.when( - R.pathEq(['key'], 'previous_year_change'), - previousYearChangeAccessor(data), - ), - R.when( - R.pathEq(['key'], 'previous_year_percentage'), - previousYearPercentageAccessor(data), - ), - // Pervious period. - R.when(R.pathEq(['key'], 'previous_period'), previousPeriodAccessor(data)), - R.when( - R.pathEq(['key'], 'previous_period_change'), - previousPeriodChangeAccessor(data), - ), - R.when( - R.pathEq(['key'], 'previous_period_percentage'), - previousPeriodPercentageAccessor(data), - ), - )(column); -}); - -/** - * Total sub-columns composer. - */ -const totalColumnsComposer = R.curry((data, column) => { - return R.map(totalColumnsMapper(data), column.children); -}); - -/** - * Detarmines the given string starts with `date-range` string. - */ -const isMatchesDateRange = (r) => R.match(/^date-range/g, r).length > 0; - -/** - * Dynamic column mapper. - */ -const dynamicColumnMapper = R.curry((data, column) => { - const indexTotalMapper = totalMapper(data); - const indexAccountNameMapper = accountNameMapper(data); - const indexDatePeriodMapper = dateRangeMapper(data); - - return R.compose( - R.when(R.pathSatisfies(isMatchesDateRange, ['key']), indexDatePeriodMapper), - R.when(R.pathEq(['key'], 'name'), indexAccountNameMapper), - R.when(R.pathEq(['key'], 'total'), indexTotalMapper), - )(column); -}); - -/** - * Cash flow dynamic columns. - */ -export const dynamicColumns = (columns, data) => { - return R.map(dynamicColumnMapper(data), columns); -}; - export const handlePreviousYearCheckBoxChange = R.curry((form, event) => { const isChecked = event.currentTarget.checked; - form.setFieldValue('previous_year', isChecked); - form.setFieldValue('previous_year_amount_change', isChecked); - form.setFieldValue('previous_year_percentage_change', isChecked); + form.setFieldValue('previousYear', isChecked); + + if (!isChecked) { + form.setFieldValue('previousYearAmountChange', isChecked); + form.setFieldValue('previousYearPercentageChange', isChecked); + } }); +/** + * Handles previous period checkbox change. + */ export const handlePreviousPeriodCheckBoxChange = R.curry((form, event) => { const isChecked = event.currentTarget.checked; - form.setFieldValue('previous_period', isChecked); - form.setFieldValue('previous_period_amount_change', isChecked); - form.setFieldValue('previous_period_amount_change', isChecked); + form.setFieldValue('previousPeriod', isChecked); + + if (!isChecked) { + form.setFieldValue('previousPeriodAmountChange', isChecked); + form.setFieldValue('previousPeriodPercentageChange', isChecked); + } }); + +/** + * Handles previous year change checkbox change. + */ +export const handlePreviousYearChangeCheckboxChange = R.curry((form, event) => { + const isChecked = event.currentTarget.checked; + + if (isChecked) { + form.setFieldValue('previousYear', event.currentTarget.checked); + } + form.setFieldValue('previousYearAmountChange', event.currentTarget.checked); +}); + +/** + * Handles preivous year percentage checkbox change. + */ +export const handlePreviousYearPercentageCheckboxChange = R.curry( + (form, event) => { + const isChecked = event.currentTarget.checked; + + if (isChecked) { + form.setFieldValue('previousYear', event.currentTarget.checked); + } + form.setFieldValue('previousYearPercentageChange', isChecked); + }, +); + +/** + * Handles previous period percentage checkbox change. + */ +export const handlePreivousPeriodPercentageCheckboxChange = R.curry( + (form, event) => { + const isChecked = event.currentTarget.checked; + + if (isChecked) { + form.setFieldValue('previousPeriod', isChecked); + } + form.setFieldValue('previousPeriodPercentageChange', isChecked); + }, +); + +/** + * Handle previous period change checkbox change. + */ +export const handlePreviousPeriodChangeCheckboxChange = R.curry( + (form, event) => { + const isChecked = event.currentTarget.checked; + + if (isChecked) { + form.setFieldValue('previousPeriod', isChecked); + } + form.setFieldValue('previousPeriodAmountChange', isChecked); + }, +); diff --git a/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossProvider.js b/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossProvider.js index 81aa7a99c..6ae218172 100644 --- a/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossProvider.js +++ b/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossProvider.js @@ -5,6 +5,10 @@ import { transformFilterFormToQuery } from '../common'; const ProfitLossSheetContext = createContext(); +/** + * Profit/loss sheet provider. + * @returns {React.JSX} + */ function ProfitLossSheetProvider({ query, ...props }) { const { data: profitLossSheet, diff --git a/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheet.js b/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheet.js index 7a251da38..7be48b748 100644 --- a/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheet.js +++ b/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheet.js @@ -1,6 +1,6 @@ -import React, { useState } from 'react'; +import React from 'react'; import moment from 'moment'; -import { compose } from 'utils'; +import * as R from 'ramda'; import ProfitLossSheetHeader from './ProfitLossSheetHeader'; import ProfitLossActionsBar from './ProfitLossActionsBar'; @@ -10,37 +10,35 @@ import DashboardPageContent from 'components/Dashboard/DashboardPageContent'; import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withProfitLossActions from './withProfitLossActions'; +import { useProfitLossSheetQuery } from './utils'; import { ProfitLossSheetProvider } from './ProfitLossProvider'; -import { ProfitLossSheetLoadingBar, ProfitLossSheetAlerts } from './components'; +import { ProfitLossSheetLoadingBar } from './components'; import { ProfitLossBody } from './ProfitLossBody'; /** * Profit/Loss financial statement sheet. + * @returns {React.JSX} */ function ProfitLossSheet({ // #withProfitLossActions toggleProfitLossFilterDrawer: toggleDisplayFilterDrawer, }) { - const [filter, setFilter] = useState({ - basis: 'cash', - fromDate: moment().startOf('year').format('YYYY-MM-DD'), - toDate: moment().endOf('year').format('YYYY-MM-DD'), - displayColumnsType: 'total', - filterByOption: 'with-transactions', - }); + // Profit/loss sheet query. + const { query, setLocationQuery } = useProfitLossSheetQuery(); + // Handle submit filter. const handleSubmitFilter = (filter) => { - const _filter = { + const newFilter = { ...filter, fromDate: moment(filter.fromDate).format('YYYY-MM-DD'), toDate: moment(filter.toDate).format('YYYY-MM-DD'), }; - setFilter(_filter); + setLocationQuery(newFilter); }; // Handle number format submit. const handleNumberFormatSubmit = (numberFormat) => { - setFilter({ - ...filter, + setLocationQuery({ + ...query, numberFormat, }); }; @@ -53,9 +51,9 @@ function ProfitLossSheet({ ); return ( - + @@ -63,7 +61,7 @@ function ProfitLossSheet({ @@ -72,7 +70,7 @@ function ProfitLossSheet({ ); } -export default compose( +export default R.compose( withDashboardActions, withProfitLossActions, )(ProfitLossSheet); diff --git a/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheetHeader.js b/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheetHeader.js index 1a958a447..b94abca64 100644 --- a/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheetHeader.js +++ b/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheetHeader.js @@ -1,11 +1,10 @@ import React from 'react'; import moment from 'moment'; import { Formik, Form } from 'formik'; -import { FormattedMessage as T } from 'components'; -import intl from 'react-intl-universal'; -import * as Yup from 'yup'; +import * as R from 'ramda'; import { Tabs, Tab, Button, Intent } from '@blueprintjs/core'; +import { FormattedMessage as T } from 'components'; import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader'; import ProfitLossSheetHeaderGeneralPane from './ProfitLossSheetHeaderGeneralPane'; import ProfitLossSheetHeaderComparisonPanel from './ProfitLossSheetHeaderComparisonPanel'; @@ -13,8 +12,12 @@ import ProfitLossSheetHeaderComparisonPanel from './ProfitLossSheetHeaderCompari import withProfitLoss from './withProfitLoss'; import withProfitLossActions from './withProfitLossActions'; -import { compose } from 'utils'; +import { useProfitLossHeaderValidationSchema } from './utils'; +/** + * Profit/loss header. + * @returns {React.JSX} + */ function ProfitLossHeader({ // #ownProps pageFilter, @@ -27,15 +30,7 @@ function ProfitLossHeader({ toggleProfitLossFilterDrawer: toggleFilterDrawer, }) { // Validation schema. - const validationSchema = Yup.object().shape({ - fromDate: Yup.date().required().label(intl.get('from_date')), - toDate: Yup.date() - .min(Yup.ref('fromDate')) - .required() - .label(intl.get('to_date')), - filterByOption: Yup.string(), - displayColumnsType: Yup.string(), - }); + const validationSchema = useProfitLossHeaderValidationSchema(); // Initial values. const initialValues = { @@ -43,13 +38,11 @@ function ProfitLossHeader({ fromDate: moment(pageFilter.fromDate).toDate(), toDate: moment(pageFilter.toDate).toDate(), }; - // Handle form submit. const handleSubmit = (values, actions) => { onSubmitFilter(values); toggleFilterDrawer(false); }; - // Handles the cancel button click. const handleCancelClick = () => { toggleFilterDrawer(false); @@ -59,7 +52,7 @@ function ProfitLossHeader({ toggleFilterDrawer(false); }; - return ( + return ( ({ profitLossDrawerFilter, })), diff --git a/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheetHeaderComparisonPanel.js b/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheetHeaderComparisonPanel.js index 76f8a8fb7..27025e744 100644 --- a/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheetHeaderComparisonPanel.js +++ b/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheetHeaderComparisonPanel.js @@ -9,6 +9,10 @@ import { Row, Col, FieldHint } from '../../../components'; import { handlePreviousYearCheckBoxChange, handlePreviousPeriodCheckBoxChange, + handlePreviousYearChangeCheckboxChange, + handlePreviousYearPercentageCheckboxChange, + handlePreviousPeriodChangeCheckboxChange, + handlePreviousPeriodPercentageCheckboxChange, } from './utils'; /** @@ -18,7 +22,7 @@ export default function ProfitLossSheetHeaderComparisonPanel() { return ( {/**----------- Previous Year -----------*/} - + {({ form, field }) => ( }> - - {({ form: { setFieldValue }, field }) => ( + + {({ form, field }) => ( }> } {...field} - onChange={({ currentTarget }) => { - setFieldValue('previous_year', currentTarget.checked); - setFieldValue( - 'previous_year_amount_change', - currentTarget.checked, - ); - }} + onChange={handlePreviousYearChangeCheckboxChange(form)} /> )} - - {({ form: { setFieldValue }, field }) => ( + + {({ form, field }) => ( }> } {...field} - onChange={({ currentTarget }) => { - setFieldValue('previous_year', currentTarget.checked); - setFieldValue( - 'previous_year_percentage_change', - currentTarget.checked, - ); - }} + onChange={handlePreviousYearPercentageCheckboxChange(form)} /> )} @@ -76,7 +68,7 @@ export default function ProfitLossSheetHeaderComparisonPanel() { {/**----------- Previous Period (PP) -----------*/} - + {({ form, field }) => ( }> - - {({ form: { setFieldValue }, field }) => ( + + {({ form, field }) => ( }> } {...field} - onChange={({ currentTarget }) => { - setFieldValue('previous_period', currentTarget.checked); - setFieldValue( - 'previous_period_amount_change', - currentTarget.checked, - ); - }} + onChange={handlePreviousPeriodChangeCheckboxChange(form)} /> )} - - {({ form: { setFieldValue }, field }) => ( + + {({ form, field }) => ( }> } {...field} - onChange={({ currentTarget }) => { - setFieldValue('previous_period', currentTarget.checked); - setFieldValue( - 'previous_period_percentage_change', - currentTarget.checked, - ); - }} + onChange={handlePreviousPeriodPercentageCheckboxChange(form)} /> )} @@ -137,7 +114,7 @@ export default function ProfitLossSheetHeaderComparisonPanel() { {/**----------- % of Column -----------*/} - + {({ field }) => ( }> {/**----------- % of Row -----------*/} - + {({ field }) => ( }> {/**----------- % of Expense -----------*/} - + {({ field }) => ( }> {/**----------- % of Income -----------*/} - + {({ field }) => ( }> `cells[${index}].value`; + +const getReportColWidth = (data, accessor, labelText) => { + return getColumnWidth( + data, + accessor, + { magicSpacing: 10, minWidth: 100 }, + labelText, + ); +}; + +const isNodeHasChildren = (node) => !isEmpty(node.children); + +/** + * `Percentage of income` column accessor. + */ +const percentageOfIncomeAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * `Percentage of expense` column accessor. + */ +const percentageOfExpenseAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * `Percentage of column` column accessor. + */ +const percentageOfColumnAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * `Percentage of row` column accessor. + */ +const percentageOfRowAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * Previous year column accessor. + */ +const previousYearAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * Pervious year change column accessor. + */ +const previousYearChangeAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * Previous year percentage column accessor. + */ +const previousYearPercentageAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * Previous period column accessor. + */ +const previousPeriodAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * Previous period change column accessor. + */ +const previousPeriodChangeAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * Previous period percentage column accessor. + */ +const previousPeriodPercentageAccessor = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + Header: column.label, + key: column.key, + accessor, + width, + align: Align.Right, + disableSortBy: true, + textOverview: true, + }; +}); + +/** + * + * @param {*} column + * @param {*} index + * @returns + */ +const totalColumnsMapper = R.curry((data, column) => { + return R.compose( + R.when(R.pathEq(['key'], 'total'), totalColumn(data)), + // Percetage of column/row. + R.when( + R.pathEq(['key'], 'percentage_column'), + percentageOfColumnAccessor(data), + ), + R.when(R.pathEq(['key'], 'percentage_row'), percentageOfRowAccessor(data)), + R.when( + R.pathEq(['key'], 'percentage_income'), + percentageOfIncomeAccessor(data), + ), + R.when( + R.pathEq(['key'], 'percentage_expenses'), + percentageOfExpenseAccessor(data), + ), + // Previous year. + R.when(R.pathEq(['key'], 'previous_year'), previousYearAccessor(data)), + R.when( + R.pathEq(['key'], 'previous_year_change'), + previousYearChangeAccessor(data), + ), + R.when( + R.pathEq(['key'], 'previous_year_percentage'), + previousYearPercentageAccessor(data), + ), + // Pervious period. + R.when(R.pathEq(['key'], 'previous_period'), previousPeriodAccessor(data)), + R.when( + R.pathEq(['key'], 'previous_period_change'), + previousPeriodChangeAccessor(data), + ), + R.when( + R.pathEq(['key'], 'previous_period_percentage'), + previousPeriodPercentageAccessor(data), + ), + )(column); +}); + +/** + * Total sub-columns composer. + */ +const totalColumnsComposer = R.curry((data, column) => { + return R.map(totalColumnsMapper(data), column.children); +}); + +/** + * Assoc columns to total column. + */ +const assocColumnsToTotalColumn = R.curry((data, column, columnAccessor) => { + const columns = totalColumnsComposer(data, column); + + return R.assoc('columns', columns, columnAccessor); +}); + +/** + * Retrieves the total column. + */ +const totalColumn = R.curry((data, column) => { + const hasChildren = isNodeHasChildren(column); + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + key: column.key, + Header: column.label, + accessor, + textOverview: true, + Cell: CellTextSpan, + width, + disableSortBy: true, + align: hasChildren ? Align.Center : Align.Right, + }; +}); + +/** + * + */ +const totalColumnCompose = R.curry((data, column) => { + const hasChildren = isNodeHasChildren(column); + + return R.compose( + R.when(R.always(hasChildren), assocColumnsToTotalColumn(data, column)), + totalColumn(data), + )(column); +}); + +/** + * Account name column mapper. + */ +const accountNameColumn = R.curry((data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + const width = getReportColWidth(data, accessor, column.label); + + return { + key: column.key, + Header: column.label, + accessor, + className: column.key, + textOverview: true, + width: Math.max(width, 300), + sticky: Align.Left, + }; +}); + +/** + * + * @param {*} data + * @param {*} column + * @returns + */ +const dateRangeSoloColumnAttrs = (data, column) => { + const accessor = getTableCellValueAccessor(column.cell_index); + + return { + accessor, + width: getReportColWidth(data, accessor), + }; +}; + +/** + * Retrieves date range column. + */ +const dateRangeColumn = R.curry((data, column) => { + const isDateColumnHasColumns = isNodeHasChildren(column); + + const columnAccessor = { + Header: column.label, + key: column.key, + disableSortBy: true, + textOverview: true, + align: isDateColumnHasColumns ? Align.Center : Align.Right, + }; + return R.compose( + R.when( + R.always(isDateColumnHasColumns), + assocColumnsToTotalColumn(data, column), + ), + R.when( + R.always(!isDateColumnHasColumns), + R.mergeLeft(dateRangeSoloColumnAttrs(data, column)), + ), + )(columnAccessor); +}); + +/** + * Detarmines the given string starts with `date-range` string. + */ +const isMatchesDateRange = (r) => R.match(/^date-range/g, r).length > 0; + +/** + * + * @param {} data + * @param {} column + */ +const dynamicColumnMapper = R.curry((data, column) => { + const indexTotalColumn = totalColumnCompose(data); + const indexAccountNameColumn = accountNameColumn(data); + const indexDatePeriodMapper = dateRangeColumn(data); + + return R.compose( + R.when(R.pathSatisfies(isMatchesDateRange, ['key']), indexDatePeriodMapper), + R.when(R.pathEq(['key'], 'name'), indexAccountNameColumn), + R.when(R.pathEq(['key'], 'total'), indexTotalColumn), + )(column); +}); + +/** + * + * @param {*} columns + * @param {*} data + * @returns + */ +export const dynamicColumns = (columns, data) => { + return R.map(dynamicColumnMapper(data), columns); +}; diff --git a/src/containers/FinancialStatements/ProfitLossSheet/hooks.js b/src/containers/FinancialStatements/ProfitLossSheet/hooks.js index 6f89ce860..aab7adb02 100644 --- a/src/containers/FinancialStatements/ProfitLossSheet/hooks.js +++ b/src/containers/FinancialStatements/ProfitLossSheet/hooks.js @@ -1,7 +1,12 @@ import React from 'react'; -import { dynamicColumns } from './utils'; + +import { dynamicColumns } from './dynamicColumns'; import { useProfitLossSheetContext } from './ProfitLossProvider'; +/** + * Retrieves the profit/loss table columns. + * @returns + */ export const useProfitLossSheetColumns = () => { const { profitLossSheet: { table }, diff --git a/src/containers/FinancialStatements/ProfitLossSheet/utils.js b/src/containers/FinancialStatements/ProfitLossSheet/utils.js index 2426e45ab..a010ac632 100644 --- a/src/containers/FinancialStatements/ProfitLossSheet/utils.js +++ b/src/containers/FinancialStatements/ProfitLossSheet/utils.js @@ -1,397 +1,159 @@ +import React from 'react'; import * as R from 'ramda'; -import { isEmpty } from 'lodash'; +import moment from 'moment'; +import intl from 'react-intl-universal'; +import * as Yup from 'yup'; -import { Align } from 'common'; -import { CellTextSpan } from 'components/Datatable/Cells'; -import { getColumnWidth } from 'utils'; - -const getTableCellValueAccessor = (index) => `cells[${index}].value`; - -const getReportColWidth = (data, accessor, labelText) => { - return getColumnWidth( - data, - accessor, - { magicSpacing: 10, minWidth: 100 }, - labelText, - ); -}; - -const isNodeHasChildren = (node) => !isEmpty(node.children); +import { useMutateLocationQuery, useLocationQuery } from 'hooks'; +import { transformToForm } from 'utils'; /** - * `Percentage of income` column accessor. - */ -const percentageOfIncomeAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * `Percentage of expense` column accessor. - */ -const percentageOfExpenseAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * `Percentage of column` column accessor. - */ -const percentageOfColumnAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * `Percentage of row` column accessor. - */ -const percentageOfRowAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * Previous year column accessor. - */ -const previousYearAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * Pervious year change column accessor. - */ -const previousYearChangeAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * Previous year percentage column accessor. - */ -const previousYearPercentageAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * Previous period column accessor. - */ -const previousPeriodAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * Previous period change column accessor. - */ -const previousPeriodChangeAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * Previous period percentage column accessor. - */ -const previousPeriodPercentageAccessor = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - Header: column.label, - key: column.key, - accessor, - width, - align: Align.Right, - disableSortBy: true, - textOverview: true, - }; -}); - -/** - * - * @param {*} column - * @param {*} index + * Retrieves the default profit/loss sheet query. * @returns */ -const totalColumnsMapper = R.curry((data, column) => { - return R.compose( - R.when(R.pathEq(['key'], 'total'), totalColumn(data)), - // Percetage of column/row. - R.when( - R.pathEq(['key'], 'percentage_column'), - percentageOfColumnAccessor(data), - ), - R.when(R.pathEq(['key'], 'percentage_row'), percentageOfRowAccessor(data)), - R.when( - R.pathEq(['key'], 'percentage_income'), - percentageOfIncomeAccessor(data), - ), - R.when( - R.pathEq(['key'], 'percentage_expenses'), - percentageOfExpenseAccessor(data), - ), - // Previous year. - R.when(R.pathEq(['key'], 'previous_year'), previousYearAccessor(data)), - R.when( - R.pathEq(['key'], 'previous_year_change'), - previousYearChangeAccessor(data), - ), - R.when( - R.pathEq(['key'], 'previous_year_percentage'), - previousYearPercentageAccessor(data), - ), - // Pervious period. - R.when(R.pathEq(['key'], 'previous_period'), previousPeriodAccessor(data)), - R.when( - R.pathEq(['key'], 'previous_period_change'), - previousPeriodChangeAccessor(data), - ), - R.when( - R.pathEq(['key'], 'previous_period_percentage'), - previousPeriodPercentageAccessor(data), - ), - )(column); +export const getDefaultProfitLossQuery = () => ({ + basis: 'cash', + fromDate: moment().startOf('year').format('YYYY-MM-DD'), + toDate: moment().endOf('year').format('YYYY-MM-DD'), + displayColumnsType: 'total', + filterByOption: 'with-transactions', + + previousYear: false, + previousYearAmountChange: false, + previousYearPercentageChange: false, + + previousPeriod: false, + previousPeriodAmountChange: false, + previousPeriodPercentageChange: false, + + // Percentage columns. + percentageColumn: false, + percentageRow: false, + percentageIncome: false, + percentageExpense: false, }); /** - * Total sub-columns composer. + * Retrieves the balance sheet query API. */ -const totalColumnsComposer = R.curry((data, column) => { - return R.map(totalColumnsMapper(data), column.children); -}); +export const useProfitLossSheetQuery = () => { + // Retrieves location query. + const locationQuery = useLocationQuery(); -/** - * Assoc columns to total column. - */ -const assocColumnsToTotalColumn = R.curry((data, column, columnAccessor) => { - const columns = totalColumnsComposer(data, column); + // Mutate the location query. + const { mutate: setLocationQuery } = useMutateLocationQuery(); - return R.assoc('columns', columns, columnAccessor); -}); + // Merges the default query with location query. + const query = React.useMemo(() => { + const defaultQuery = getDefaultProfitLossQuery(); -/** - * Retrieves the total column. - */ -const totalColumn = R.curry((data, column) => { - const hasChildren = isNodeHasChildren(column); - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); + return { + ...defaultQuery, + ...transformToForm(Object.fromEntries([...locationQuery]), defaultQuery), + }; + }, [locationQuery]); return { - key: column.key, - Header: column.label, - accessor, - textOverview: true, - Cell: CellTextSpan, - width, - disableSortBy: true, - align: hasChildren ? Align.Center : Align.Right, - }; -}); - -/** - * - */ -const totalColumnCompose = R.curry((data, column) => { - const hasChildren = isNodeHasChildren(column); - - return R.compose( - R.when(R.always(hasChildren), assocColumnsToTotalColumn(data, column)), - totalColumn(data), - )(column); -}); - -/** - * Account name column mapper. - */ -const accountNameColumn = R.curry((data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - const width = getReportColWidth(data, accessor, column.label); - - return { - key: column.key, - Header: column.label, - accessor, - className: column.key, - textOverview: true, - width: Math.max(width, 300), - sticky: Align.Left, - }; -}); - -/** - * - * @param {*} data - * @param {*} column - * @returns - */ -const dateRangeSoloColumnAttrs = (data, column) => { - const accessor = getTableCellValueAccessor(column.cell_index); - - return { - accessor, - width: getReportColWidth(data, accessor), + query, + locationQuery, + setLocationQuery, }; }; /** - * Retrieves date range column. - */ -const dateRangeColumn = R.curry((data, column) => { - const isDateColumnHasColumns = isNodeHasChildren(column); - - const columnAccessor = { - Header: column.label, - key: column.key, - disableSortBy: true, - textOverview: true, - align: isDateColumnHasColumns ? Align.Center : Align.Right, - }; - return R.compose( - R.when( - R.always(isDateColumnHasColumns), - assocColumnsToTotalColumn(data, column), - ), - R.when( - R.always(!isDateColumnHasColumns), - R.mergeLeft(dateRangeSoloColumnAttrs(data, column)), - ), - )(columnAccessor); -}); - -/** - * Detarmines the given string starts with `date-range` string. - */ -const isMatchesDateRange = (r) => R.match(/^date-range/g, r).length > 0; - -/** - * - * @param {} data - * @param {} column - */ -const dynamicColumnMapper = R.curry((data, column) => { - const indexTotalColumn = totalColumnCompose(data); - const indexAccountNameColumn = accountNameColumn(data); - const indexDatePeriodMapper = dateRangeColumn(data); - - return R.compose( - R.when(R.pathSatisfies(isMatchesDateRange, ['key']), indexDatePeriodMapper), - R.when(R.pathEq(['key'], 'name'), indexAccountNameColumn), - R.when(R.pathEq(['key'], 'total'), indexTotalColumn), - )(column); -}); - -/** - * - * @param {*} columns - * @param {*} data + * Retrieves the profit/loss header validation schema. * @returns */ -export const dynamicColumns = (columns, data) => { - return R.map(dynamicColumnMapper(data), columns); +export const useProfitLossHeaderValidationSchema = () => { + return Yup.object().shape({ + fromDate: Yup.date().required().label(intl.get('from_date')), + toDate: Yup.date() + .min(Yup.ref('fromDate')) + .required() + .label(intl.get('to_date')), + filterByOption: Yup.string(), + displayColumnsType: Yup.string(), + }); }; +/** + * Handles the previous year checkbox change. + */ export const handlePreviousYearCheckBoxChange = R.curry((form, event) => { const isChecked = event.currentTarget.checked; - form.setFieldValue('previous_year', isChecked); - form.setFieldValue('previous_year_amount_change', isChecked); - form.setFieldValue('previous_year_percentage_change', isChecked); + + form.setFieldValue('previousYear', isChecked); + + if (!isChecked) { + form.setFieldValue('previousYearAmountChange', isChecked); + form.setFieldValue('previousYearPercentageChange', isChecked); + } }); +/** + * Handles the preivous period checkbox change. + */ export const handlePreviousPeriodCheckBoxChange = R.curry((form, event) => { const isChecked = event.currentTarget.checked; - form.setFieldValue('previous_period', isChecked); - form.setFieldValue('previous_period_amount_change', isChecked); - form.setFieldValue('previous_period_amount_change', isChecked); + + form.setFieldValue('previousPeriod', isChecked); + + if (!isChecked) { + form.setFieldValue('previousPeriodAmountChange', isChecked); + form.setFieldValue('previousPeriodPercentageChange', isChecked); + } }); + +/** + * Handles previous year change amount checkbox change. + */ +export const handlePreviousYearChangeCheckboxChange = R.curry((form, event) => { + const isChecked = event.currentTarget.checked; + + if (isChecked) { + form.setFieldValue('previousYear', isChecked); + } + form.setFieldValue('previousYearAmountChange', isChecked); +}); + +/** + * Handle previous year percentage checkbox change. + */ +export const handlePreviousYearPercentageCheckboxChange = R.curry( + (form, event) => { + const isChecked = event.currentTarget.checked; + + if (isChecked) { + form.setFieldValue('previousYear', isChecked); + } + form.setFieldValue('previousYearPercentageChange', isChecked); + }, +); + +/** + * Handles previous period change amout checkbox change. + */ +export const handlePreviousPeriodChangeCheckboxChange = R.curry( + (form, event) => { + const isChecked = event.currentTarget.checked; + + if (isChecked) { + form.setFieldValue('previousPeriod', isChecked); + } + form.setFieldValue('previousPeriodAmountChange', isChecked); + }, +); + +/** + * Handles previous period percentage checkbox change. + */ +export const handlePreviousPeriodPercentageCheckboxChange = R.curry( + (form, event) => { + const isChecked = event.currentTarget.checked; + + if (isChecked) { + form.setFieldValue('previousPeriod', isChecked); + } + form.setFieldValue('previousPeriodPercentageChange', isChecked); + }, +); diff --git a/src/hooks/index.js b/src/hooks/index.js index 03418357d..cc22b5491 100644 --- a/src/hooks/index.js +++ b/src/hooks/index.js @@ -1,4 +1,5 @@ import { useRef, useEffect, useMemo } from 'react'; +import { useLocation, useHistory } from 'react-router'; import useAutofocus from './useAutofocus'; import { useLocalStorage } from './utils/useLocalStorage'; @@ -53,3 +54,29 @@ export function useMemorizedColumnsWidths(tableName) { }; return [get, save, handleColumnResizing]; } + +/** + * Retrieve the URL location search params. + */ +export const useLocationQuery = () => { + const { search } = useLocation(); + + return useMemo(() => { + return new URLSearchParams(search); + }, [search]); +}; + +/** + * Mutates the URL location params. + */ +export const useMutateLocationQuery = () => { + const location = useLocation(); + const history = useHistory(); + + return { + mutate: (query) => { + const params = new URLSearchParams(query).toString(); + history.push({ pathname: location.pathname, search: params.toString() }); + }, + }; +};