mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 13:50:31 +00:00
feat: balance sheet report.
feat: trial balance sheet. feat: general ledger report. feat: journal report. feat: profit/loss report.
This commit is contained in:
@@ -4,6 +4,7 @@ import { compose } from 'utils';
|
||||
import { useQuery } from 'react-query';
|
||||
import moment from 'moment';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { queryCache } from 'react-query';
|
||||
|
||||
import BalanceSheetHeader from './BalanceSheetHeader';
|
||||
import BalanceSheetTable from './BalanceSheetTable';
|
||||
@@ -18,66 +19,74 @@ import withSettings from 'containers/Settings/withSettings';
|
||||
import withBalanceSheetActions from './withBalanceSheetActions';
|
||||
import withBalanceSheetDetail from './withBalanceSheetDetail';
|
||||
|
||||
import { transformFilterFormToQuery } from 'containers/FinancialStatements/common';
|
||||
|
||||
function BalanceSheet({
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
setDashboardBackLink,
|
||||
|
||||
// #withBalanceSheetActions
|
||||
fetchBalanceSheet,
|
||||
refreshBalanceSheet,
|
||||
|
||||
// #withBalanceSheetDetail
|
||||
balanceSheetFilter,
|
||||
balanceSheetRefresh,
|
||||
|
||||
// #withPreferences
|
||||
organizationSettings,
|
||||
organizationName,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const [filter, setFilter] = useState({
|
||||
from_date: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
to_date: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
basis: 'cash',
|
||||
display_columns_type: 'total',
|
||||
display_columns_by: '',
|
||||
none_zero: false,
|
||||
displayColumnsType: 'total',
|
||||
accountsFilter: 'all-accounts',
|
||||
});
|
||||
const [refresh, setRefresh] = useState(true);
|
||||
|
||||
const fetchHook = useQuery(
|
||||
['balance-sheet', filter],
|
||||
(key, query) => fetchBalanceSheet({ ...query }),
|
||||
{ manual: true },
|
||||
// Fetches the balance sheet.
|
||||
const fetchHook = useQuery(['balance-sheet', filter], (key, query) =>
|
||||
fetchBalanceSheet({ ...transformFilterFormToQuery(query) }),
|
||||
);
|
||||
|
||||
// Handle fetch the data of balance sheet.
|
||||
const handleFetchData = useCallback(() => {
|
||||
setRefresh(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
changePageTitle(formatMessage({ id: 'balance_sheet' }));
|
||||
}, [changePageTitle, formatMessage]);
|
||||
|
||||
// Observes the balance sheet refresh to invalid the query to refresh it.
|
||||
useEffect(() => {
|
||||
if (balanceSheetRefresh) {
|
||||
queryCache.invalidateQueries('balance-sheet');
|
||||
refreshBalanceSheet(false);
|
||||
}
|
||||
}, [balanceSheetRefresh, refreshBalanceSheet]);
|
||||
|
||||
useEffect(() => {
|
||||
// Show the back link on dashboard topbar.
|
||||
setDashboardBackLink(true);
|
||||
|
||||
return () => {
|
||||
// Hide the back link on dashboard topbar.
|
||||
setDashboardBackLink(false);
|
||||
};
|
||||
});
|
||||
|
||||
// Handle re-fetch balance sheet after filter change.
|
||||
const handleFilterSubmit = useCallback(
|
||||
(filter) => {
|
||||
const _filter = {
|
||||
...filter,
|
||||
from_date: moment(filter.from_date).format('YYYY-MM-DD'),
|
||||
to_date: moment(filter.to_date).format('YYYY-MM-DD'),
|
||||
fromDate: moment(filter.fromDate).format('YYYY-MM-DD'),
|
||||
toDate: moment(filter.toDate).format('YYYY-MM-DD'),
|
||||
};
|
||||
setFilter({ ..._filter });
|
||||
setRefresh(true);
|
||||
refreshBalanceSheet(true);
|
||||
},
|
||||
[setFilter],
|
||||
[setFilter, refreshBalanceSheet],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (refresh) {
|
||||
fetchHook.refetch({ force: true });
|
||||
setRefresh(false);
|
||||
}
|
||||
}, [refresh]);
|
||||
|
||||
return (
|
||||
<DashboardInsider>
|
||||
<BalanceSheetActionsBar />
|
||||
@@ -87,15 +96,9 @@ function BalanceSheet({
|
||||
<BalanceSheetHeader
|
||||
pageFilter={filter}
|
||||
onSubmitFilter={handleFilterSubmit}
|
||||
show={balanceSheetFilter}
|
||||
/>
|
||||
|
||||
<div class="financial-statement__body">
|
||||
<BalanceSheetTable
|
||||
companyName={organizationSettings.name}
|
||||
balanceSheetQuery={filter}
|
||||
onFetchData={handleFetchData}
|
||||
/>
|
||||
<BalanceSheetTable companyName={organizationName} />
|
||||
</div>
|
||||
</FinancialStatement>
|
||||
</DashboardPageContent>
|
||||
@@ -106,8 +109,10 @@ function BalanceSheet({
|
||||
export default compose(
|
||||
withDashboardActions,
|
||||
withBalanceSheetActions,
|
||||
withBalanceSheetDetail(({ balanceSheetFilter }) => ({
|
||||
balanceSheetFilter,
|
||||
withBalanceSheetDetail(({ balanceSheetRefresh }) => ({
|
||||
balanceSheetRefresh,
|
||||
})),
|
||||
withSettings(({ organizationSettings }) => ({
|
||||
organizationName: organizationSettings.name,
|
||||
})),
|
||||
withSettings,
|
||||
)(BalanceSheet);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
NavbarGroup,
|
||||
Button,
|
||||
@@ -13,26 +13,24 @@ import classNames from 'classnames';
|
||||
|
||||
import Icon from 'components/Icon';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
import FilterDropdown from 'components/FilterDropdown';
|
||||
import { If } from 'components';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import withBalanceSheetDetail from './withBalanceSheetDetail';
|
||||
import withBalanceSheetActions from './withBalanceSheetActions';
|
||||
|
||||
|
||||
function BalanceSheetActionsBar({
|
||||
// #withBalanceSheetDetail
|
||||
balanceSheetFilter,
|
||||
|
||||
// #withBalanceSheetActions
|
||||
toggleBalanceSheetFilter,
|
||||
refreshBalanceSheet
|
||||
refreshBalanceSheet,
|
||||
}) {
|
||||
const handleFilterToggleClick = () => {
|
||||
toggleBalanceSheetFilter();
|
||||
};
|
||||
|
||||
// Handle recalculate the report button.
|
||||
const handleRecalcReport = () => {
|
||||
refreshBalanceSheet(true);
|
||||
};
|
||||
@@ -41,39 +39,21 @@ function BalanceSheetActionsBar({
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--table-views')}
|
||||
icon={<Icon icon="cog-16" iconSize={16} />}
|
||||
text={<T id={'customize_report'} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
className={classNames(
|
||||
Classes.MINIMAL,
|
||||
'button--gray-highlight',
|
||||
)}
|
||||
className={classNames(Classes.MINIMAL, 'button--gray-highlight')}
|
||||
text={<T id={'recalc_report'} />}
|
||||
onClick={handleRecalcReport}
|
||||
icon={<Icon icon="refresh-16" iconSize={16} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<If condition={balanceSheetFilter}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={<T id={'hide_filter'} />}
|
||||
onClick={handleFilterToggleClick}
|
||||
icon={<Icon icon="arrow-to-top" />}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<If condition={!balanceSheetFilter}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={<T id={'show_filter'} />}
|
||||
onClick={handleFilterToggleClick}
|
||||
icon={<Icon icon="arrow-to-bottom" />}
|
||||
/>
|
||||
</If>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--table-views')}
|
||||
icon={<Icon icon="cog-16" iconSize={16} />}
|
||||
text={!balanceSheetFilter ? <T id={'customize_report'} /> : <T id={'hide_customizer'} />}
|
||||
onClick={handleFilterToggleClick}
|
||||
active={balanceSheetFilter}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Popover
|
||||
// content={}
|
||||
@@ -91,7 +71,7 @@ function BalanceSheetActionsBar({
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon='print-16' iconSize={16} />}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
text={<T id={'print'} />}
|
||||
/>
|
||||
<Button
|
||||
@@ -107,4 +87,4 @@ function BalanceSheetActionsBar({
|
||||
export default compose(
|
||||
withBalanceSheetDetail(({ balanceSheetFilter }) => ({ balanceSheetFilter })),
|
||||
withBalanceSheetActions,
|
||||
)(BalanceSheetActionsBar);
|
||||
)(BalanceSheetActionsBar);
|
||||
|
||||
@@ -1,126 +1,105 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
|
||||
import { Row, Col, Visible } from 'react-grid-system';
|
||||
import { FormGroup } from '@blueprintjs/core';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Tabs, Tab, Button, Intent } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import moment from 'moment';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { Formik, Form } from 'formik';
|
||||
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy';
|
||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||
import FinancialAccountsFilter from '../FinancialAccountsFilter';
|
||||
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
|
||||
|
||||
import withBalanceSheet from './withBalanceSheetDetail';
|
||||
import withBalanceSheetActions from './withBalanceSheetActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import BalanceSheetHeaderGeneralPanal from './BalanceSheetHeaderGeneralPanal';
|
||||
|
||||
function BalanceSheetHeader({
|
||||
// #ownProps
|
||||
onSubmitFilter,
|
||||
pageFilter,
|
||||
show,
|
||||
refresh,
|
||||
|
||||
// #withBalanceSheet
|
||||
balanceSheetFilter,
|
||||
|
||||
// #withBalanceSheetActions
|
||||
refreshBalanceSheet,
|
||||
toggleBalanceSheetFilter,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
...pageFilter,
|
||||
basis: 'cash',
|
||||
from_date: moment(pageFilter.from_date).toDate(),
|
||||
to_date: moment(pageFilter.to_date).toDate(),
|
||||
none_zero: false,
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
from_date: Yup.date()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'from_data' })),
|
||||
to_date: Yup.date()
|
||||
.min(Yup.ref('from_date'))
|
||||
.required()
|
||||
.label(formatMessage({ id: 'to_date' })),
|
||||
none_zero: Yup.boolean(),
|
||||
}),
|
||||
onSubmit: (values, actions) => {
|
||||
onSubmitFilter(values);
|
||||
actions.setSubmitting(false);
|
||||
},
|
||||
// Filter form initial values.
|
||||
const initialValues = {
|
||||
basis: 'cash',
|
||||
...pageFilter,
|
||||
fromDate: moment(pageFilter.fromDate).toDate(),
|
||||
toDate: moment(pageFilter.toDate).toDate(),
|
||||
};
|
||||
|
||||
// Validation schema.
|
||||
const validationSchema = Yup.object().shape({
|
||||
dateRange: Yup.string().optional(),
|
||||
fromDate: Yup.date()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'fromDate' })),
|
||||
toDate: Yup.date()
|
||||
.min(Yup.ref('fromDate'))
|
||||
.required()
|
||||
.label(formatMessage({ id: 'toDate' })),
|
||||
accountsFilter: Yup.string(),
|
||||
displayColumnsType: Yup.string(),
|
||||
});
|
||||
|
||||
// Handle item select of `display columns by` field.
|
||||
const onItemSelectDisplayColumns = useCallback(
|
||||
(item) => {
|
||||
formik.setFieldValue('display_columns_type', item.type);
|
||||
formik.setFieldValue('display_columns_by', item.by);
|
||||
},
|
||||
[formik],
|
||||
);
|
||||
// Handle form submit.
|
||||
const handleSubmit = (values, actions) => {
|
||||
onSubmitFilter(values);
|
||||
toggleBalanceSheetFilter();
|
||||
actions.setSubmitting(false);
|
||||
};
|
||||
|
||||
const handleAccountingBasisChange = useCallback(
|
||||
(value) => {
|
||||
formik.setFieldValue('basis', value);
|
||||
},
|
||||
[formik],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (refresh) {
|
||||
formik.submitForm();
|
||||
refreshBalanceSheet(false);
|
||||
}
|
||||
}, [refresh]);
|
||||
|
||||
const handleAccountsFilterSelect = (filterType) => {
|
||||
const noneZero = filterType.key === 'without-zero-balance' ? true : false;
|
||||
formik.setFieldValue('none_zero', noneZero);
|
||||
// Handle cancel button click.
|
||||
const handleCancelClick = () => {
|
||||
toggleBalanceSheetFilter();
|
||||
};
|
||||
// Handle drawer close action.
|
||||
const handleDrawerClose = () => {
|
||||
toggleBalanceSheetFilter();
|
||||
};
|
||||
|
||||
return (
|
||||
<FinancialStatementHeader show={show}>
|
||||
<Row>
|
||||
<FinancialStatementDateRange formik={formik} />
|
||||
|
||||
<Visible xl>
|
||||
<Col width={'100%'} />
|
||||
</Visible>
|
||||
|
||||
<Col width={260} offset={10}>
|
||||
<SelectDisplayColumnsBy onItemSelect={onItemSelectDisplayColumns} />
|
||||
</Col>
|
||||
|
||||
<Col width={260}>
|
||||
<FormGroup
|
||||
label={<T id={'filter_accounts'} />}
|
||||
className="form-group--select-list bp3-fill"
|
||||
inline={false}
|
||||
>
|
||||
<FinancialAccountsFilter
|
||||
initialSelectedItem={'all-accounts'}
|
||||
onItemSelect={handleAccountsFilterSelect}
|
||||
<FinancialStatementHeader
|
||||
isOpen={balanceSheetFilter}
|
||||
drawerProps={{ onClose: handleDrawerClose }}
|
||||
>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Form>
|
||||
<Tabs animate={true} vertical={true} renderActiveTabPanelOnly={true}>
|
||||
<Tab
|
||||
id="general"
|
||||
title={<T id={'general'} />}
|
||||
panel={<BalanceSheetHeaderGeneralPanal />}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Tabs>
|
||||
|
||||
<Col width={260}>
|
||||
<RadiosAccountingBasis
|
||||
selectedValue={formik.values.basis}
|
||||
onChange={handleAccountingBasisChange}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<div class="financial-header-drawer__footer">
|
||||
<Button className={'mr1'} intent={Intent.PRIMARY} type={'submit'}>
|
||||
<T id={'calculate_report'} />
|
||||
</Button>
|
||||
<Button onClick={handleCancelClick} minimal={true}>
|
||||
<T id={'cancel'} />
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Formik>
|
||||
</FinancialStatementHeader>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withBalanceSheet(({ balanceSheetRefresh }) => ({
|
||||
refresh: balanceSheetRefresh,
|
||||
withBalanceSheet(({ balanceSheetFilter }) => ({
|
||||
balanceSheetFilter,
|
||||
})),
|
||||
withBalanceSheetActions,
|
||||
)(BalanceSheetHeader);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy';
|
||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||
import FinancialAccountsFilter from '../FinancialAccountsFilter';
|
||||
|
||||
/**
|
||||
* Balance sheet header - General panal.
|
||||
*/
|
||||
export default function BalanceSheetHeaderGeneralTab({}) {
|
||||
return (
|
||||
<div>
|
||||
<FinancialStatementDateRange />
|
||||
<SelectDisplayColumnsBy />
|
||||
<FinancialAccountsFilter
|
||||
initialSelectedItem={'all-accounts'}
|
||||
/>
|
||||
<RadiosAccountingBasis key={'basis'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,31 +1,56 @@
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { useIntl } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
|
||||
|
||||
import Money from 'components/Money';
|
||||
import FinancialSheet from 'components/FinancialSheet';
|
||||
import DataTable from 'components/DataTable';
|
||||
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withBalanceSheetDetail from './withBalanceSheetDetail';
|
||||
import { getFinancialSheetIndexByQuery } from 'store/financialStatement/financialStatements.selectors';
|
||||
|
||||
import { compose, defaultExpanderReducer } from 'utils';
|
||||
import { compose, defaultExpanderReducer, getColumnWidth } from 'utils';
|
||||
|
||||
// Total cell.
|
||||
function TotalCell({ cell }) {
|
||||
const row = cell.row.original;
|
||||
|
||||
if (row.total) {
|
||||
return (
|
||||
<Money
|
||||
amount={row.total.formatted_amount}
|
||||
currency={row.total.currency_code}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
// Total period cell.
|
||||
const TotalPeriodCell = (index) => ({ cell }) => {
|
||||
const { original } = cell.row;
|
||||
|
||||
if (original.total_periods && original.total_periods[index]) {
|
||||
const amount = original.total_periods[index].formatted_amount;
|
||||
const currencyCode = original.total_periods[index].currency_code;
|
||||
|
||||
return <Money amount={amount} currency={currencyCode} />;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Balance sheet table.
|
||||
*/
|
||||
function BalanceSheetTable({
|
||||
// #withPreferences
|
||||
organizationSettings,
|
||||
|
||||
// #withBalanceSheetDetail
|
||||
balanceSheetAccounts,
|
||||
balanceSheetTableRows,
|
||||
balanceSheetColumns,
|
||||
balanceSheetQuery,
|
||||
balanceSheetLoading,
|
||||
|
||||
// #ownProps
|
||||
onFetchData,
|
||||
companyName,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
@@ -33,35 +58,18 @@ function BalanceSheetTable({
|
||||
() => [
|
||||
{
|
||||
Header: formatMessage({ id: 'account_name' }),
|
||||
accessor: 'name',
|
||||
accessor: (row) => (row.code ? `${row.name} - ${row.code}` : row.name),
|
||||
className: 'account_name',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
Header: formatMessage({ id: 'code' }),
|
||||
accessor: 'code',
|
||||
className: 'code',
|
||||
width: 60,
|
||||
width: 240,
|
||||
},
|
||||
...(balanceSheetQuery.display_columns_type === 'total'
|
||||
? [
|
||||
{
|
||||
Header: formatMessage({ id: 'total' }),
|
||||
accessor: 'balance.formatted_amount',
|
||||
Cell: ({ cell }) => {
|
||||
const row = cell.row.original;
|
||||
if (row.total) {
|
||||
return (
|
||||
<Money
|
||||
amount={row.total.formatted_amount}
|
||||
currency={'USD'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return '';
|
||||
},
|
||||
Cell: TotalCell,
|
||||
className: 'total',
|
||||
width: 80,
|
||||
width: 140,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
@@ -70,44 +78,43 @@ function BalanceSheetTable({
|
||||
id: `date_period_${index}`,
|
||||
Header: column,
|
||||
accessor: `total_periods[${index}]`,
|
||||
Cell: ({ cell }) => {
|
||||
const { original } = cell.row;
|
||||
if (original.total_periods && original.total_periods[index]) {
|
||||
const amount = original.total_periods[index].formatted_amount;
|
||||
return <Money amount={amount} currency={'USD'} />;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
Cell: TotalPeriodCell(index),
|
||||
className: classNames('total-period', `total-periods-${index}`),
|
||||
width: 80,
|
||||
width: getColumnWidth(
|
||||
balanceSheetTableRows,
|
||||
`total_periods.${index}.formatted_amount`,
|
||||
{ minWidth: 100 },
|
||||
),
|
||||
}))
|
||||
: []),
|
||||
],
|
||||
[balanceSheetQuery, balanceSheetColumns, formatMessage],
|
||||
[balanceSheetQuery, balanceSheetColumns, balanceSheetTableRows, formatMessage],
|
||||
);
|
||||
|
||||
const handleFetchData = useCallback(() => {
|
||||
onFetchData && onFetchData();
|
||||
}, [onFetchData]);
|
||||
|
||||
// Calculates the default expanded rows of balance sheet table.
|
||||
const expandedRows = useMemo(
|
||||
() => defaultExpanderReducer(balanceSheetTableRows, 3),
|
||||
() => defaultExpanderReducer(balanceSheetTableRows, 4),
|
||||
[balanceSheetTableRows],
|
||||
);
|
||||
|
||||
const rowClassNames = (row) => {
|
||||
const rowClassNames = useCallback((row) => {
|
||||
const { original } = row;
|
||||
console.log(row);
|
||||
const rowTypes = Array.isArray(original.row_types)
|
||||
? original.row_types
|
||||
: [];
|
||||
|
||||
return {
|
||||
[`row_type--${original.row_type}`]: original.row_type,
|
||||
...rowTypes.reduce((acc, rowType) => {
|
||||
acc[`row_type--${rowType}`] = rowType;
|
||||
return acc;
|
||||
}, {}),
|
||||
};
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<FinancialSheet
|
||||
name="balance-sheet"
|
||||
companyName={organizationSettings.name}
|
||||
companyName={companyName}
|
||||
sheetType={formatMessage({ id: 'balance_sheet' })}
|
||||
fromDate={balanceSheetQuery.from_date}
|
||||
toDate={balanceSheetQuery.to_date}
|
||||
@@ -119,46 +126,29 @@ function BalanceSheetTable({
|
||||
columns={columns}
|
||||
data={balanceSheetTableRows}
|
||||
rowClassNames={rowClassNames}
|
||||
onFetchData={handleFetchData}
|
||||
noInitialFetch={true}
|
||||
expanded={expandedRows}
|
||||
expandable={true}
|
||||
expanded={expandedRows}
|
||||
expandToggleColumn={1}
|
||||
sticky={true}
|
||||
expandColumnSpace={0.8}
|
||||
sticky={true}
|
||||
/>
|
||||
</FinancialSheet>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const { balanceSheetQuery } = props;
|
||||
return {
|
||||
balanceSheetIndex: getFinancialSheetIndexByQuery(
|
||||
state.financialStatements.balanceSheet.sheets,
|
||||
balanceSheetQuery,
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const withBalanceSheetTable = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withBalanceSheetTable,
|
||||
withBalanceSheetDetail(
|
||||
({
|
||||
balanceSheetAccounts,
|
||||
balanceSheetTableRows,
|
||||
balanceSheetColumns,
|
||||
balanceSheetQuery,
|
||||
balanceSheetLoading,
|
||||
}) => ({
|
||||
balanceSheetAccounts,
|
||||
balanceSheetTableRows,
|
||||
balanceSheetColumns,
|
||||
balanceSheetQuery,
|
||||
balanceSheetLoading,
|
||||
}),
|
||||
),
|
||||
withSettings,
|
||||
)(BalanceSheetTable);
|
||||
|
||||
@@ -1,28 +1,36 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
getFinancialSheet,
|
||||
getFinancialSheetAccounts,
|
||||
getFinancialSheetColumns,
|
||||
getFinancialSheetQuery,
|
||||
getFinancialSheetTableRows,
|
||||
getFinancialSheetFactory,
|
||||
getFinancialSheetAccountsFactory,
|
||||
getFinancialSheetColumnsFactory,
|
||||
getFinancialSheetQueryFactory,
|
||||
getFinancialSheetTableRowsFactory,
|
||||
} from 'store/financialStatement/financialStatements.selectors';
|
||||
|
||||
|
||||
export default (mapState) => {
|
||||
const mapStateToProps = (state, props) => {
|
||||
const { balanceSheetIndex } = props;
|
||||
const getBalanceSheet = getFinancialSheetFactory('balanceSheet');
|
||||
const getBalanceSheetAccounts = getFinancialSheetAccountsFactory(
|
||||
'balanceSheet',
|
||||
);
|
||||
const getBalanceSheetTableRows = getFinancialSheetTableRowsFactory(
|
||||
'balanceSheet',
|
||||
);
|
||||
const getBalanceSheetColumns = getFinancialSheetColumnsFactory('balanceSheet');
|
||||
const getBalanceSheetQuery = getFinancialSheetQueryFactory('balanceSheet');
|
||||
|
||||
const mapped = {
|
||||
balanceSheet: getFinancialSheet(state.financialStatements.balanceSheet.sheets, balanceSheetIndex),
|
||||
balanceSheetAccounts: getFinancialSheetAccounts(state.financialStatements.balanceSheet.sheets, balanceSheetIndex),
|
||||
balanceSheetTableRows: getFinancialSheetTableRows(state.financialStatements.balanceSheet.sheets, balanceSheetIndex),
|
||||
balanceSheetColumns: getFinancialSheetColumns(state.financialStatements.balanceSheet.sheets, balanceSheetIndex),
|
||||
balanceSheetQuery: getFinancialSheetQuery(state.financialStatements.balanceSheet.sheets, balanceSheetIndex),
|
||||
balanceSheet: getBalanceSheet(state, props),
|
||||
balanceSheetAccounts: getBalanceSheetAccounts(state, props),
|
||||
balanceSheetTableRows: getBalanceSheetTableRows(state, props),
|
||||
balanceSheetColumns: getBalanceSheetColumns(state, props),
|
||||
balanceSheetQuery: getBalanceSheetQuery(state, props),
|
||||
balanceSheetLoading: state.financialStatements.balanceSheet.loading,
|
||||
balanceSheetFilter: state.financialStatements.balanceSheet.filter,
|
||||
balanceSheetRefresh: state.financialStatements.balanceSheet.refresh,
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
|
||||
|
||||
return connect(mapStateToProps);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,73 +1,70 @@
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
PopoverInteractionKind,
|
||||
Tooltip,
|
||||
MenuItem,
|
||||
Position,
|
||||
FormGroup,
|
||||
} from '@blueprintjs/core';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { ListSelect, MODIFIER } from 'components';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { FastField } from 'formik';
|
||||
|
||||
export default function FinancialAccountsFilter({
|
||||
...restProps
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
const filterAccountsOptions = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: 'all-accounts',
|
||||
name: formatMessage({ id: 'all_accounts' }),
|
||||
hint: formatMessage({ id: 'all_accounts_including_with_zero_balance' }),
|
||||
},
|
||||
{
|
||||
key: 'without-zero-balance',
|
||||
name: formatMessage({ id: 'accounts_without_zero_balance' }),
|
||||
hint: formatMessage({ id: 'include_accounts_and_exclude_zero_balance' }),
|
||||
},
|
||||
{
|
||||
key: 'with-transactions',
|
||||
name: formatMessage({ id: 'accounts_with_transactions' }),
|
||||
hint: formatMessage({ id: 'include_accounts_once_has_transactions_on_given_date_period' }),
|
||||
},
|
||||
],
|
||||
[formatMessage],
|
||||
);
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { Col, Row, ListSelect, MODIFIER } from 'components';
|
||||
import { filterAccountsOptions } from './common';
|
||||
|
||||
export default function FinancialAccountsFilter({ ...restProps }) {
|
||||
const SUBMENU_POPOVER_MODIFIERS = {
|
||||
flip: { boundariesElement: 'viewport', padding: 20 },
|
||||
offset: { offset: '0, 10' },
|
||||
preventOverflow: { boundariesElement: 'viewport', padding: 40 },
|
||||
};
|
||||
|
||||
const filterAccountRenderer = useCallback(
|
||||
(item, { handleClick, modifiers, query }) => {
|
||||
return (
|
||||
<Tooltip
|
||||
interactionKind={PopoverInteractionKind.HOVER}
|
||||
position={Position.RIGHT_TOP}
|
||||
content={item.hint}
|
||||
modifiers={SUBMENU_POPOVER_MODIFIERS}
|
||||
inline={true}
|
||||
minimal={true}
|
||||
className={MODIFIER.SELECT_LIST_TOOLTIP_ITEMS}
|
||||
>
|
||||
<MenuItem text={item.name} key={item.key} onClick={handleClick} />
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
[],
|
||||
);
|
||||
const filterAccountRenderer = (item, { handleClick, modifiers, query }) => {
|
||||
return (
|
||||
<Tooltip
|
||||
interactionKind={PopoverInteractionKind.HOVER}
|
||||
position={Position.RIGHT_TOP}
|
||||
content={item.hint}
|
||||
modifiers={SUBMENU_POPOVER_MODIFIERS}
|
||||
inline={true}
|
||||
minimal={true}
|
||||
className={MODIFIER.SELECT_LIST_TOOLTIP_ITEMS}
|
||||
>
|
||||
<MenuItem text={item.name} key={item.key} onClick={handleClick} />
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ListSelect
|
||||
items={filterAccountsOptions}
|
||||
itemRenderer={filterAccountRenderer}
|
||||
popoverProps={{ minimal: true, }}
|
||||
filterable={false}
|
||||
selectedItemProp={'key'}
|
||||
labelProp={'name'}
|
||||
// className={}
|
||||
{...restProps}
|
||||
/>
|
||||
<Row>
|
||||
<Col xs={4}>
|
||||
<FastField name={'accountsFilter'}>
|
||||
{({ form: { setFieldValue }, field: { value } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'filter_accounts'} />}
|
||||
className="form-group--select-list bp3-fill"
|
||||
inline={false}
|
||||
>
|
||||
<ListSelect
|
||||
items={filterAccountsOptions}
|
||||
itemRenderer={filterAccountRenderer}
|
||||
popoverProps={{ minimal: true }}
|
||||
filterable={false}
|
||||
selectedItem={value}
|
||||
selectedItemProp={'key'}
|
||||
labelProp={'name'}
|
||||
onItemSelect={(item) => {
|
||||
setFieldValue('accountsFilter', item.key);
|
||||
}}
|
||||
className={classNames(CLASSES.SELECT_LIST_FILL_POPOVER)}
|
||||
{...restProps}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,106 +1,120 @@
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import { Row, Col } from 'react-grid-system';
|
||||
import { momentFormatter } from 'utils';
|
||||
import React from 'react';
|
||||
import { FastField, ErrorMessage } from 'formik';
|
||||
import { HTMLSelect, FormGroup, Intent, Position } from '@blueprintjs/core';
|
||||
import moment from 'moment';
|
||||
import { Row, Col, Hint } from 'components';
|
||||
import { momentFormatter, parseDateRangeQuery } from 'utils';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { HTMLSelect, FormGroup, Intent, Position } from '@blueprintjs/core';
|
||||
import { Hint } from 'components';
|
||||
import { parseDateRangeQuery } from 'utils';
|
||||
import { dateRangeOptions } from 'containers/FinancialStatements/common';
|
||||
|
||||
export default function FinancialStatementDateRange({ formik }) {
|
||||
const intl = useIntl();
|
||||
const [reportDateRange, setReportDateRange] = useState('this_year');
|
||||
|
||||
const dateRangeOptions = useMemo(
|
||||
() => [
|
||||
{ value: 'today', label: 'Today' },
|
||||
{ value: 'this_week', label: 'This Week' },
|
||||
{ value: 'this_month', label: 'This Month' },
|
||||
{ value: 'this_quarter', label: 'This Quarter' },
|
||||
{ value: 'this_year', label: 'This Year' },
|
||||
{ value: 'custom', label: 'Custom Range' },
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
const handleDateChange = useCallback(
|
||||
(name) => (date) => {
|
||||
setReportDateRange('custom');
|
||||
formik.setFieldValue(name, date);
|
||||
},
|
||||
[setReportDateRange, formik],
|
||||
);
|
||||
|
||||
// Handles date range field change.
|
||||
const handleDateRangeChange = useCallback(
|
||||
(e) => {
|
||||
const value = e.target.value;
|
||||
if (value !== 'custom') {
|
||||
const dateRange = parseDateRangeQuery(value);
|
||||
if (dateRange) {
|
||||
formik.setFieldValue('from_date', dateRange.from_date);
|
||||
formik.setFieldValue('to_date', dateRange.to_date);
|
||||
}
|
||||
}
|
||||
setReportDateRange(value);
|
||||
},
|
||||
[formik],
|
||||
);
|
||||
/**
|
||||
* Financial statement - Date range select.
|
||||
*/
|
||||
export default function FinancialStatementDateRange() {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Col width={260}>
|
||||
<FormGroup
|
||||
label={intl.formatMessage({ id: 'report_date_range' })}
|
||||
labelInfo={<Hint />}
|
||||
minimal={true}
|
||||
fill={true}
|
||||
>
|
||||
<HTMLSelect
|
||||
fill={true}
|
||||
options={dateRangeOptions}
|
||||
value={reportDateRange}
|
||||
onChange={handleDateRangeChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
<Row>
|
||||
<Col xs={4}>
|
||||
<FastField name={'date_range'}>
|
||||
{({
|
||||
form: { setFieldValue },
|
||||
field: { value },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={formatMessage({ id: 'report_date_range' })}
|
||||
labelInfo={<Hint />}
|
||||
minimal={true}
|
||||
fill={true}
|
||||
>
|
||||
<HTMLSelect
|
||||
fill={true}
|
||||
options={dateRangeOptions}
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
const newValue = e.target.value;
|
||||
|
||||
<Col width={260}>
|
||||
<FormGroup
|
||||
label={intl.formatMessage({ id: 'from_date' })}
|
||||
labelInfo={<Hint />}
|
||||
fill={true}
|
||||
intent={formik.errors.from_date && Intent.DANGER}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={formik.values.from_date}
|
||||
onChange={handleDateChange('from_date')}
|
||||
popoverProps={{ position: Position.BOTTOM }}
|
||||
minimal={true}
|
||||
fill={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
if (newValue !== 'custom') {
|
||||
const dateRange = parseDateRangeQuery(newValue);
|
||||
|
||||
<Col width={260}>
|
||||
<FormGroup
|
||||
label={intl.formatMessage({ id: 'to_date' })}
|
||||
labelInfo={<Hint />}
|
||||
fill={true}
|
||||
intent={formik.errors.to_date && Intent.DANGER}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={formik.values.to_date}
|
||||
onChange={handleDateChange('to_date')}
|
||||
popoverProps={{ position: Position.BOTTOM }}
|
||||
fill={true}
|
||||
minimal={true}
|
||||
intent={formik.errors.to_date && Intent.DANGER}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
if (dateRange) {
|
||||
setFieldValue('fromDate', moment(dateRange.fromDate).toDate());
|
||||
setFieldValue('toDate', moment(dateRange.toDate).toDate());
|
||||
}
|
||||
}
|
||||
setFieldValue('dateRange', newValue);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Col xs={4}>
|
||||
<FastField name={'fromDate'}>
|
||||
{({
|
||||
form: { setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={formatMessage({ id: 'from_date' })}
|
||||
labelInfo={<Hint />}
|
||||
fill={true}
|
||||
intent={error && Intent.DANGER}
|
||||
helperText={<ErrorMessage name={'fromDate'} />}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY-MM-DD')}
|
||||
value={value}
|
||||
onChange={(selectedDate) => {
|
||||
setFieldValue('fromDate', selectedDate);
|
||||
}}
|
||||
popoverProps={{ minimal: true, position: Position.BOTTOM }}
|
||||
canClearSelection={false}
|
||||
minimal={true}
|
||||
fill={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
|
||||
<Col xs={4}>
|
||||
<FastField name={'toDate'}>
|
||||
{({
|
||||
form: { setFieldValue },
|
||||
field: { value },
|
||||
meta: { error },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={formatMessage({ id: 'to_date' })}
|
||||
labelInfo={<Hint />}
|
||||
fill={true}
|
||||
intent={error && Intent.DANGER}
|
||||
helperText={<ErrorMessage name={'toDate'} />}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY-MM-DD')}
|
||||
value={value}
|
||||
onChange={(selectedDate) => {
|
||||
setFieldValue('toDate', selectedDate);
|
||||
}}
|
||||
popoverProps={{ minimal: true, position: Position.BOTTOM }}
|
||||
canClearSelection={false}
|
||||
fill={true}
|
||||
minimal={true}
|
||||
intent={error && Intent.DANGER}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,59 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Position, Drawer } from '@blueprintjs/core';
|
||||
|
||||
export default function FinancialStatementHeader({
|
||||
children,
|
||||
isOpen,
|
||||
drawerProps,
|
||||
}) {
|
||||
const timeoutRef = React.useRef();
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
|
||||
// Hides the content scrollbar and scroll to the top of the page once the drawer open.
|
||||
useEffect(() => {
|
||||
const contentPanel = document.querySelector('body');
|
||||
contentPanel.classList.toggle('hide-scrollbar', isOpen);
|
||||
|
||||
if (isOpen) {
|
||||
document.querySelector('.Pane2').scrollTo(0, 0);
|
||||
}
|
||||
return () => {
|
||||
contentPanel.classList.remove('hide-scrollbar');
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
clearTimeout(timeoutRef.current);
|
||||
|
||||
if (isOpen) {
|
||||
setIsDrawerOpen(isOpen);
|
||||
} else {
|
||||
timeoutRef.current = setTimeout(() => setIsDrawerOpen(isOpen), 300);
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
export default function FinancialStatementHeader({ show, children }) {
|
||||
return (
|
||||
<div
|
||||
className={classNames('financial-statement__header', {
|
||||
'is-hidden': !show,
|
||||
})}
|
||||
className={classNames(
|
||||
'financial-statement__header',
|
||||
'financial-header-drawer',
|
||||
{
|
||||
'is-hidden': !isDrawerOpen,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
<Drawer
|
||||
isOpen={isOpen}
|
||||
usePortal={false}
|
||||
hasBackdrop={true}
|
||||
position={Position.TOP}
|
||||
canOutsideClickClose={true}
|
||||
canEscapeKeyClose={true}
|
||||
{...drawerProps}
|
||||
>
|
||||
{children}
|
||||
</Drawer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import React, { useEffect, useCallback, useState} from 'react';
|
||||
import React, { useEffect, useCallback, useState } from 'react';
|
||||
import moment from 'moment';
|
||||
import GeneralLedgerTable from 'containers/FinancialStatements/GeneralLedger/GeneralLedgerTable';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { queryCache } from 'react-query';
|
||||
|
||||
import GeneralLedgerTable from 'containers/FinancialStatements/GeneralLedger/GeneralLedgerTable';
|
||||
import GeneralLedgerHeader from './GeneralLedgerHeader';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider'
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||
import GeneralLedgerActionsBar from './GeneralLedgerActionsBar';
|
||||
|
||||
@@ -17,73 +16,101 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
import { transformFilterFormToQuery } from 'containers/FinancialStatements/common';
|
||||
import withGeneralLedger from './withGeneralLedger';
|
||||
|
||||
/**
|
||||
* General Ledger (GL) sheet.
|
||||
*/
|
||||
function GeneralLedger({
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
setDashboardBackLink,
|
||||
|
||||
// #withGeneralLedgerActions
|
||||
fetchGeneralLedger,
|
||||
|
||||
refreshGeneralLedgerSheet,
|
||||
|
||||
// #withAccountsActions
|
||||
requestFetchAccounts,
|
||||
|
||||
// #withGeneralLedger
|
||||
generalLedgerSheetRefresh,
|
||||
|
||||
// #withSettings
|
||||
organizationSettings,
|
||||
organizationName,
|
||||
}) {
|
||||
const { formatMessage } = useIntl()
|
||||
const { formatMessage } = useIntl();
|
||||
const [filter, setFilter] = useState({
|
||||
from_date: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
to_date: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
basis: 'accural',
|
||||
none_zero: true,
|
||||
});
|
||||
|
||||
// Change page title of the dashboard.
|
||||
useEffect(() => {
|
||||
changePageTitle(formatMessage({id:'general_ledger'}));
|
||||
}, [changePageTitle,formatMessage]);
|
||||
changePageTitle(formatMessage({ id: 'general_ledger' }));
|
||||
}, [changePageTitle, formatMessage]);
|
||||
|
||||
const fetchAccounts = useQuery(['accounts-list'],
|
||||
() => requestFetchAccounts());
|
||||
useEffect(() => {
|
||||
// Show the back link on dashboard topbar.
|
||||
setDashboardBackLink(true);
|
||||
|
||||
const fetchSheet = useQuery(['general-ledger', filter],
|
||||
(key, query) => fetchGeneralLedger(query),
|
||||
{ manual: true });
|
||||
|
||||
// Handle fetch data of trial balance table.
|
||||
const handleFetchData = useCallback(() => {
|
||||
fetchSheet.refetch({ force: true });
|
||||
}, []);
|
||||
|
||||
// Handle financial statement filter change.
|
||||
const handleFilterSubmit = useCallback((filter) => {
|
||||
const parsedFilter = {
|
||||
...filter,
|
||||
from_date: moment(filter.from_date).format('YYYY-MM-DD'),
|
||||
to_date: moment(filter.to_date).format('YYYY-MM-DD'),
|
||||
return () => {
|
||||
// Hide the back link on dashboard topbar.
|
||||
setDashboardBackLink(false);
|
||||
};
|
||||
setFilter(parsedFilter);
|
||||
}, [setFilter]);
|
||||
});
|
||||
|
||||
const handleFilterChanged = () => { };
|
||||
// Observes the GL sheet refresh to invalid the query to refresh it.
|
||||
useEffect(() => {
|
||||
if (generalLedgerSheetRefresh) {
|
||||
queryCache.invalidateQueries('general-ledger');
|
||||
refreshGeneralLedgerSheet(false);
|
||||
}
|
||||
}, [generalLedgerSheetRefresh, refreshGeneralLedgerSheet]);
|
||||
|
||||
// Fetches accounts list.
|
||||
const fetchAccounts = useQuery(['accounts-list'], () =>
|
||||
requestFetchAccounts(),
|
||||
);
|
||||
// Fetches the general ledger sheet.
|
||||
const fetchSheet = useQuery(['general-ledger', filter], (key, q) =>
|
||||
fetchGeneralLedger({ ...transformFilterFormToQuery(q) }),
|
||||
);
|
||||
|
||||
// Handle financial statement filter change.
|
||||
const handleFilterSubmit = useCallback(
|
||||
(filter) => {
|
||||
const parsedFilter = {
|
||||
...filter,
|
||||
fromDate: moment(filter.fromDate).format('YYYY-MM-DD'),
|
||||
toDate: moment(filter.toDate).format('YYYY-MM-DD'),
|
||||
};
|
||||
setFilter(parsedFilter);
|
||||
refreshGeneralLedgerSheet(true);
|
||||
},
|
||||
[setFilter, refreshGeneralLedgerSheet],
|
||||
);
|
||||
|
||||
return (
|
||||
<DashboardInsider>
|
||||
<GeneralLedgerActionsBar
|
||||
onFilterChanged={handleFilterChanged} />
|
||||
<GeneralLedgerActionsBar />
|
||||
|
||||
<DashboardPageContent>
|
||||
<div class="financial-statement financial-statement--general-ledger">
|
||||
<GeneralLedgerHeader
|
||||
pageFilter={filter}
|
||||
onSubmitFilter={handleFilterSubmit} />
|
||||
onSubmitFilter={handleFilterSubmit}
|
||||
/>
|
||||
|
||||
<div class="financial-statement__body">
|
||||
<GeneralLedgerTable
|
||||
companyName={organizationSettings.name}
|
||||
companyName={organizationName}
|
||||
generalLedgerQuery={filter}
|
||||
onFetchData={handleFetchData} />
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</DashboardPageContent>
|
||||
@@ -95,5 +122,10 @@ export default compose(
|
||||
withGeneralLedgerActions,
|
||||
withDashboardActions,
|
||||
withAccountsActions,
|
||||
withSettings,
|
||||
)(GeneralLedger);
|
||||
withGeneralLedger(({ generalLedgerSheetRefresh }) => ({
|
||||
generalLedgerSheetRefresh,
|
||||
})),
|
||||
withSettings(({ organizationSettings }) => ({
|
||||
organizationName: organizationSettings.name,
|
||||
})),
|
||||
)(GeneralLedger);
|
||||
|
||||
@@ -9,11 +9,10 @@ import {
|
||||
Position,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import Icon from 'components/Icon';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'
|
||||
import { If } from 'components';
|
||||
import classNames from 'classnames';
|
||||
import FilterDropdown from 'components/FilterDropdown';
|
||||
|
||||
import Icon from 'components/Icon';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
|
||||
import withGeneralLedger from './withGeneralLedger';
|
||||
import withGeneralLedgerActions from './withGeneralLedgerActions';
|
||||
@@ -21,7 +20,7 @@ import withGeneralLedgerActions from './withGeneralLedgerActions';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* General ledger actions bar.
|
||||
* General ledger - Actions bar.
|
||||
*/
|
||||
function GeneralLedgerActionsBar({
|
||||
// #withGeneralLedger
|
||||
@@ -29,12 +28,13 @@ function GeneralLedgerActionsBar({
|
||||
|
||||
// #withGeneralLedgerActions
|
||||
toggleGeneralLedgerSheetFilter,
|
||||
refreshGeneralLedgerSheet
|
||||
refreshGeneralLedgerSheet,
|
||||
}) {
|
||||
const handleFilterClick = () => {
|
||||
// Handle customize button click.
|
||||
const handleCustomizeClick = () => {
|
||||
toggleGeneralLedgerSheetFilter();
|
||||
};
|
||||
|
||||
// Handle re-calculate button click.
|
||||
const handleRecalcReport = () => {
|
||||
refreshGeneralLedgerSheet(true);
|
||||
};
|
||||
@@ -43,62 +43,50 @@ function GeneralLedgerActionsBar({
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--table-views')}
|
||||
icon={<Icon icon='cog-16' iconSize={16} />}
|
||||
text={<T id={'customize_report'}/>}
|
||||
/>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
className={classNames(
|
||||
Classes.MINIMAL,
|
||||
'button--gray-highlight',
|
||||
)}
|
||||
text={'Re-calc Report'}
|
||||
className={classNames(Classes.MINIMAL, 'button--gray-highlight')}
|
||||
text={<T id={'recalc_report'} />}
|
||||
onClick={handleRecalcReport}
|
||||
icon={<Icon icon="refresh-16" iconSize={16} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<If condition={generalLedgerSheetFilter}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={<T id={'hide_filter'} />}
|
||||
icon={<Icon icon="arrow-to-top" />}
|
||||
onClick={handleFilterClick}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<If condition={!generalLedgerSheetFilter}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={<T id={'show_filter'} />}
|
||||
icon={<Icon icon="arrow-to-bottom" />}
|
||||
onClick={handleFilterClick}
|
||||
/>
|
||||
</If>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--table-views')}
|
||||
icon={<Icon icon="cog-16" iconSize={16} />}
|
||||
text={
|
||||
generalLedgerSheetFilter ? (
|
||||
<T id={'hide_customizer'} />
|
||||
) : (
|
||||
<T id={'customize_report'} />
|
||||
)
|
||||
}
|
||||
onClick={handleCustomizeClick}
|
||||
active={generalLedgerSheetFilter}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Popover
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}>
|
||||
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'}/>}
|
||||
icon={<Icon icon="filter-16" iconSize={16} /> } />
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon='print-16' iconSize={16} />}
|
||||
text={<T id={'print'}/>}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
text={<T id={'print'} />}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon='file-export-16' iconSize={16} />}
|
||||
text={<T id={'export'}/>}
|
||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||
text={<T id={'export'} />}
|
||||
/>
|
||||
</NavbarGroup>
|
||||
</DashboardActionsBar>
|
||||
@@ -106,6 +94,8 @@ function GeneralLedgerActionsBar({
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withGeneralLedger(({ generalLedgerSheetFilter }) => ({ generalLedgerSheetFilter })),
|
||||
withGeneralLedger(({ generalLedgerSheetFilter }) => ({
|
||||
generalLedgerSheetFilter,
|
||||
})),
|
||||
withGeneralLedgerActions,
|
||||
)(GeneralLedgerActionsBar);
|
||||
)(GeneralLedgerActionsBar);
|
||||
|
||||
@@ -1,113 +1,100 @@
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import { Button, FormGroup, Classes } from '@blueprintjs/core';
|
||||
import { Row, Col, Visible } from 'react-grid-system';
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { Tabs, Tab, Button, Intent } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
import AccountsMultiSelect from 'components/AccountsMultiSelect';
|
||||
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
|
||||
import withAccounts from 'containers/Accounts/withAccounts';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||
import GeneralLedgerHeaderGeneralPane from './GeneralLedgerHeaderGeneralPane';
|
||||
|
||||
import withGeneralLedger from './withGeneralLedger';
|
||||
import withGeneralLedgerActions from './withGeneralLedgerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
|
||||
/**
|
||||
* Geenral Ledger (GL) - Header.
|
||||
*/
|
||||
function GeneralLedgerHeader({
|
||||
// #ownProps
|
||||
onSubmitFilter,
|
||||
pageFilter,
|
||||
|
||||
// #withAccounts
|
||||
accountsList,
|
||||
|
||||
// #withGeneralLedgerActions
|
||||
refreshGeneralLedgerSheet,
|
||||
toggleGeneralLedgerSheetFilter,
|
||||
|
||||
// #withGeneralLedger
|
||||
generalLedgerSheetFilter,
|
||||
generalLedgerSheetRefresh
|
||||
}) {
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
...pageFilter,
|
||||
from_date: moment(pageFilter.from_date).toDate(),
|
||||
to_date: moment(pageFilter.to_date).toDate(),
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
from_date: Yup.date().required(),
|
||||
to_date: Yup.date().min(Yup.ref('from_date')).required(),
|
||||
}),
|
||||
onSubmit(values, actions) {
|
||||
onSubmitFilter(values);
|
||||
actions.setSubmitting(false);
|
||||
},
|
||||
// Initial values.
|
||||
const initialValues = {
|
||||
...pageFilter,
|
||||
fromDate: moment(pageFilter.fromDate).toDate(),
|
||||
toDate: moment(pageFilter.toDate).toDate(),
|
||||
};
|
||||
|
||||
// Validation schema.
|
||||
const validationSchema = Yup.object().shape({
|
||||
dateRange: Yup.string().optional(),
|
||||
fromDate: Yup.date().required(),
|
||||
toDate: Yup.date().min(Yup.ref('fromDate')).required(),
|
||||
});
|
||||
|
||||
const onAccountSelected = useCallback((selectedAccounts) => {
|
||||
formik.setFieldValue('accounts_ids', Object.keys(selectedAccounts));
|
||||
}, [formik.setFieldValue]);
|
||||
// Handle form submit.
|
||||
const handleSubmit = (values, { setSubmitting }) => {
|
||||
onSubmitFilter(values);
|
||||
toggleGeneralLedgerSheetFilter();
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
const handleAccountingBasisChange = useCallback(
|
||||
(value) => {
|
||||
formik.setFieldValue('basis', value);
|
||||
},
|
||||
[formik],
|
||||
);
|
||||
|
||||
// handle submit filter submit button.
|
||||
useEffect(() => {
|
||||
if (generalLedgerSheetRefresh) {
|
||||
formik.submitForm();
|
||||
refreshGeneralLedgerSheet(false);
|
||||
}
|
||||
}, [formik, generalLedgerSheetRefresh])
|
||||
// Handle cancel button click.
|
||||
const handleCancelClick = () => {
|
||||
toggleGeneralLedgerSheetFilter(false);
|
||||
};
|
||||
|
||||
// Handle drawer close action.
|
||||
const handleDrawerClose = () => {
|
||||
toggleGeneralLedgerSheetFilter(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<FinancialStatementHeader show={generalLedgerSheetFilter}>
|
||||
<Row>
|
||||
<FinancialStatementDateRange formik={formik} />
|
||||
|
||||
<Visible xl><Col width={'100%'} /></Visible>
|
||||
|
||||
<Col width={260}>
|
||||
<FormGroup
|
||||
label={<T id={'specific_accounts'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<AccountsMultiSelect
|
||||
accounts={accountsList}
|
||||
onAccountSelected={onAccountSelected}
|
||||
<FinancialStatementHeader
|
||||
isOpen={generalLedgerSheetFilter}
|
||||
drawerProps={{ onClose: handleDrawerClose }}
|
||||
>
|
||||
<Formik
|
||||
validationSchema={validationSchema}
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Form>
|
||||
<Tabs animate={true} vertical={true} renderActiveTabPanelOnly={true}>
|
||||
<Tab
|
||||
id="general"
|
||||
title={<T id={'general'} />}
|
||||
panel={<GeneralLedgerHeaderGeneralPane />}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Tabs>
|
||||
|
||||
<Col width={260}>
|
||||
<RadiosAccountingBasis
|
||||
onChange={handleAccountingBasisChange}
|
||||
selectedValue={formik.values.basis}
|
||||
/>
|
||||
</Col>
|
||||
<div class="financial-header-drawer__footer">
|
||||
<Button className={'mr1'} intent={Intent.PRIMARY} type={'submit'}>
|
||||
<T id={'calculate_report'} />
|
||||
</Button>
|
||||
|
||||
</Row>
|
||||
<Button onClick={handleCancelClick} minimal={true}>
|
||||
<T id={'cancel'} />
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Formik>
|
||||
</FinancialStatementHeader>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAccounts(({ accountsList }) => ({
|
||||
accountsList,
|
||||
})),
|
||||
withGeneralLedger(({ generalLedgerSheetFilter, generalLedgerSheetRefresh }) => ({
|
||||
withGeneralLedger(({ generalLedgerSheetFilter }) => ({
|
||||
generalLedgerSheetFilter,
|
||||
generalLedgerSheetRefresh,
|
||||
})),
|
||||
withGeneralLedgerActions,
|
||||
)(GeneralLedgerHeader);
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import { FormGroup, Classes } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { AccountsMultiSelect, Row, Col } from 'components';
|
||||
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||
|
||||
import withAccounts from 'containers/Accounts/withAccounts';
|
||||
|
||||
import { compose } from 'redux';
|
||||
|
||||
/**
|
||||
* General ledger (GL) - Header - General panel.
|
||||
*/
|
||||
function GeneralLedgerHeaderGeneralPane({
|
||||
// #withAccounts
|
||||
accountsList,
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<FinancialStatementDateRange />
|
||||
|
||||
<Row>
|
||||
<Col xs={4}>
|
||||
<FormGroup
|
||||
label={<T id={'specific_accounts'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
>
|
||||
<AccountsMultiSelect accounts={accountsList} />
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<RadiosAccountingBasis key={'basis'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAccounts(({ accountsList }) => ({ accountsList })))(
|
||||
GeneralLedgerHeaderGeneralPane,
|
||||
);
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import moment from 'moment';
|
||||
import { connect } from 'react-redux';
|
||||
import { defaultExpanderReducer, compose } from 'utils';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
@@ -8,7 +7,6 @@ import FinancialSheet from 'components/FinancialSheet';
|
||||
import DataTable from 'components/DataTable';
|
||||
import Money from 'components/Money';
|
||||
|
||||
import { getFinancialSheetIndexByQuery } from 'store/financialStatement/financialStatements.selectors';
|
||||
import withGeneralLedger from './withGeneralLedger';
|
||||
|
||||
const ROW_TYPE = {
|
||||
@@ -20,7 +18,6 @@ const ROW_TYPE = {
|
||||
|
||||
function GeneralLedgerTable({
|
||||
companyName,
|
||||
onFetchData,
|
||||
|
||||
generalLedgerSheetLoading,
|
||||
generalLedgerTableRows,
|
||||
@@ -29,35 +26,29 @@ function GeneralLedgerTable({
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
// Account name column accessor.
|
||||
const accountNameAccessor = useCallback(
|
||||
(row) => {
|
||||
switch (row.rowType) {
|
||||
case ROW_TYPE.OPENING_BALANCE:
|
||||
return 'Opening Balance';
|
||||
case ROW_TYPE.CLOSING_BALANCE:
|
||||
return 'Closing Balance';
|
||||
default:
|
||||
return row.name;
|
||||
}
|
||||
},
|
||||
[ROW_TYPE],
|
||||
);
|
||||
const accountNameAccessor = (row) => {
|
||||
switch (row.rowType) {
|
||||
case ROW_TYPE.OPENING_BALANCE:
|
||||
return 'Opening Balance';
|
||||
case ROW_TYPE.CLOSING_BALANCE:
|
||||
return 'Closing Balance';
|
||||
default:
|
||||
return row.name;
|
||||
}
|
||||
};
|
||||
|
||||
// Date accessor.
|
||||
const dateAccessor = useCallback(
|
||||
(row) => {
|
||||
const TYPES = [
|
||||
ROW_TYPE.OPENING_BALANCE,
|
||||
ROW_TYPE.CLOSING_BALANCE,
|
||||
ROW_TYPE.TRANSACTION,
|
||||
];
|
||||
const dateAccessor = (row) => {
|
||||
const TYPES = [
|
||||
ROW_TYPE.OPENING_BALANCE,
|
||||
ROW_TYPE.CLOSING_BALANCE,
|
||||
ROW_TYPE.TRANSACTION,
|
||||
];
|
||||
|
||||
return TYPES.indexOf(row.rowType) !== -1
|
||||
? moment(row.date).format('DD MMM YYYY')
|
||||
: '';
|
||||
},
|
||||
[moment, ROW_TYPE],
|
||||
);
|
||||
return TYPES.indexOf(row.rowType) !== -1
|
||||
? moment(row.date).format('DD MMM YYYY')
|
||||
: '';
|
||||
};
|
||||
|
||||
// Amount cell
|
||||
const amountCell = useCallback(({ cell }) => {
|
||||
@@ -73,10 +64,6 @@ function GeneralLedgerTable({
|
||||
return <Money amount={transaction.amount} currency={'USD'} />;
|
||||
}, []);
|
||||
|
||||
const referenceLink = useCallback((row) => {
|
||||
return <a href="">{row.referenceId}</a>;
|
||||
});
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
@@ -99,7 +86,7 @@ function GeneralLedgerTable({
|
||||
},
|
||||
{
|
||||
Header: formatMessage({ id: 'trans_num' }),
|
||||
accessor: referenceLink,
|
||||
accessor: 'reference_id',
|
||||
className: 'transaction_number',
|
||||
width: 110,
|
||||
},
|
||||
@@ -125,10 +112,6 @@ function GeneralLedgerTable({
|
||||
[],
|
||||
);
|
||||
|
||||
const handleFetchData = useCallback(() => {
|
||||
onFetchData && onFetchData();
|
||||
}, [onFetchData]);
|
||||
|
||||
// Default expanded rows of general ledger table.
|
||||
const expandedRows = useMemo(
|
||||
() => defaultExpanderReducer(generalLedgerTableRows, 1),
|
||||
@@ -140,12 +123,11 @@ function GeneralLedgerTable({
|
||||
return (
|
||||
<FinancialSheet
|
||||
companyName={companyName}
|
||||
// sheetType={formatMessage({ id: 'general_ledger_sheet' })}
|
||||
sheetType={formatMessage({ id: 'general_ledger_sheet' })}
|
||||
fromDate={generalLedgerQuery.from_date}
|
||||
toDate={generalLedgerQuery.to_date}
|
||||
name="general-ledger"
|
||||
loading={generalLedgerSheetLoading}
|
||||
minimal={true}
|
||||
fullWidth={true}
|
||||
>
|
||||
<DataTable
|
||||
@@ -155,7 +137,6 @@ function GeneralLedgerTable({
|
||||
})}
|
||||
columns={columns}
|
||||
data={generalLedgerTableRows}
|
||||
onFetchData={handleFetchData}
|
||||
rowClassNames={rowClassNames}
|
||||
expanded={expandedRows}
|
||||
virtualizedRows={true}
|
||||
@@ -169,21 +150,7 @@ function GeneralLedgerTable({
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const { generalLedgerQuery } = props;
|
||||
|
||||
return {
|
||||
generalLedgerIndex: getFinancialSheetIndexByQuery(
|
||||
state.financialStatements.generalLedger.sheets,
|
||||
generalLedgerQuery,
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const withGeneralLedgerTable = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withGeneralLedgerTable,
|
||||
withGeneralLedger(
|
||||
({
|
||||
generalLedgerTableRows,
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
getFinancialSheet,
|
||||
getFinancialSheetQuery,
|
||||
getFinancialSheetTableRows,
|
||||
getFinancialSheetFactory,
|
||||
getFinancialSheetQueryFactory,
|
||||
getFinancialSheetTableRowsFactory,
|
||||
} from 'store/financialStatement/financialStatements.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const mapStateToProps = (state, props) => {
|
||||
const { generalLedgerIndex } = props;
|
||||
const getGeneralLedgerSheet = getFinancialSheetFactory('generalLedger');
|
||||
const getSheetTableRows = getFinancialSheetTableRowsFactory('generalLedger');
|
||||
const getSheetQuery = getFinancialSheetQueryFactory('generalLedger');
|
||||
|
||||
const mapped = {
|
||||
generalLedgerSheet: getFinancialSheet(
|
||||
state.financialStatements.generalLedger.sheets,
|
||||
generalLedgerIndex,
|
||||
),
|
||||
generalLedgerTableRows: getFinancialSheetTableRows(
|
||||
state.financialStatements.generalLedger.sheets,
|
||||
generalLedgerIndex,
|
||||
),
|
||||
generalLedgerQuery: getFinancialSheetQuery(
|
||||
state.financialStatements.generalLedger.sheets,
|
||||
generalLedgerIndex,
|
||||
),
|
||||
generalLedgerSheet: getGeneralLedgerSheet(state, props),
|
||||
generalLedgerTableRows: getSheetTableRows(state, props),
|
||||
generalLedgerQuery: getSheetQuery(state, props),
|
||||
generalLedgerSheetLoading:
|
||||
state.financialStatements.generalLedger.loading,
|
||||
generalLedgerSheetFilter: state.financialStatements.generalLedger.filter,
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useState, useCallback, useEffect } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import moment from 'moment';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { queryCache } from 'react-query';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import JournalTable from './JournalTable';
|
||||
@@ -12,79 +13,89 @@ import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
|
||||
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withJournalActions from './withJournalActions';
|
||||
import withJournal from './withJournal';
|
||||
|
||||
import { transformFilterFormToQuery } from 'containers/FinancialStatements/common';
|
||||
|
||||
function Journal({
|
||||
// #withJournalActions
|
||||
requestFetchJournalSheet,
|
||||
refreshJournalSheet,
|
||||
|
||||
// #withJournal
|
||||
journalSheetRefresh,
|
||||
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
setDashboardBackLink,
|
||||
|
||||
// #withPreferences
|
||||
organizationSettings,
|
||||
organizationName,
|
||||
}) {
|
||||
const [filter, setFilter] = useState({
|
||||
from_date: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
to_date: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
basis: 'accural',
|
||||
});
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const fetchJournalSheet = useQuery(['journal-sheet', filter], (key, query) =>
|
||||
requestFetchJournalSheet({
|
||||
...transformFilterFormToQuery(filter),
|
||||
}),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
changePageTitle(formatMessage({ id: 'journal_sheet' }));
|
||||
}, [changePageTitle, formatMessage]);
|
||||
|
||||
const fetchHook = useQuery(
|
||||
['journal', filter],
|
||||
(key, query) => requestFetchJournalSheet(query),
|
||||
{ manual: true },
|
||||
);
|
||||
useEffect(() => {
|
||||
// Show the back link on dashboard topbar.
|
||||
setDashboardBackLink(true);
|
||||
|
||||
return () => {
|
||||
// Hide the back link on dashboard topbar.
|
||||
setDashboardBackLink(false);
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (journalSheetRefresh) {
|
||||
queryCache.invalidateQueries('journal-sheet');
|
||||
refreshJournalSheet(false);
|
||||
}
|
||||
}, [journalSheetRefresh, refreshJournalSheet]);
|
||||
|
||||
// Handle financial statement filter change.
|
||||
const handleFilterSubmit = useCallback(
|
||||
(filter) => {
|
||||
const _filter = {
|
||||
...filter,
|
||||
from_date: moment(filter.from_date).format('YYYY-MM-DD'),
|
||||
to_date: moment(filter.to_date).format('YYYY-MM-DD'),
|
||||
fromDate: moment(filter.fromDate).format('YYYY-MM-DD'),
|
||||
toDate: moment(filter.toDate).format('YYYY-MM-DD'),
|
||||
};
|
||||
setFilter(_filter);
|
||||
fetchHook.refetch({ force: true });
|
||||
queryCache.invalidateQueries('journal-sheet');
|
||||
},
|
||||
[fetchHook],
|
||||
[setFilter],
|
||||
);
|
||||
|
||||
const handlePrintClick = useCallback(() => {}, []);
|
||||
|
||||
const handleExportClick = useCallback(() => {}, []);
|
||||
|
||||
const handleFetchData = useCallback(({ sortBy, pageIndex, pageSize }) => {
|
||||
fetchHook.refetch({ force: true });
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DashboardInsider>
|
||||
<JournalActionsBar
|
||||
onSubmitFilter={handleFilterSubmit}
|
||||
onPrintClick={handlePrintClick}
|
||||
onExportClick={handleExportClick}
|
||||
/>
|
||||
<JournalActionsBar />
|
||||
|
||||
<DashboardPageContent>
|
||||
<div class="financial-statement financial-statement--journal">
|
||||
<JournalHeader
|
||||
pageFilter={filter}
|
||||
onSubmitFilter={handleFilterSubmit}
|
||||
pageFilter={filter}
|
||||
/>
|
||||
|
||||
<div class="financial-statement__body">
|
||||
<JournalTable
|
||||
companyName={organizationSettings.name}
|
||||
companyName={organizationName}
|
||||
journalQuery={filter}
|
||||
onFetchData={handleFetchData}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,5 +107,10 @@ function Journal({
|
||||
export default compose(
|
||||
withDashboardActions,
|
||||
withJournalActions,
|
||||
withSettings,
|
||||
withSettings(({ organizationSettings }) => ({
|
||||
organizationName: organizationSettings.name,
|
||||
})),
|
||||
withJournal(({ journalSheetRefresh }) => ({
|
||||
journalSheetRefresh,
|
||||
})),
|
||||
)(Journal);
|
||||
|
||||
@@ -11,16 +11,16 @@ import {
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import Icon from 'components/Icon';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
import FilterDropdown from 'components/FilterDropdown';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { If } from 'components';
|
||||
|
||||
import withJournalActions from './withJournalActions';
|
||||
import withJournal from './withJournal';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Journal sheeet - Actions bar.
|
||||
*/
|
||||
function JournalActionsBar({
|
||||
// #withJournal
|
||||
journalSheetFilter,
|
||||
@@ -40,36 +40,28 @@ function JournalActionsBar({
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--table-views')}
|
||||
icon={<Icon icon="cog-16" iconSize={16} />}
|
||||
text={<T id={'customize_report'} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--gray-highlight')}
|
||||
text={<T id={'recalc_report'} />}
|
||||
onClick={handleRecalcReport}
|
||||
icon={<Icon icon="refresh-16" iconSize={16} />}
|
||||
/>
|
||||
<If condition={journalSheetFilter}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={<T id={'hide_filter'} />}
|
||||
icon={<Icon icon="arrow-to-top" />}
|
||||
onClick={handleFilterToggleClick}
|
||||
/>
|
||||
</If>
|
||||
<NavbarDivider />
|
||||
|
||||
<If condition={!journalSheetFilter}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={<T id={'show_filter'} />}
|
||||
icon={<Icon icon="arrow-to-bottom" />}
|
||||
onClick={handleFilterToggleClick}
|
||||
/>
|
||||
</If>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--table-views')}
|
||||
icon={<Icon icon="cog-16" iconSize={16} />}
|
||||
text={
|
||||
(journalSheetFilter) ? (
|
||||
<T id={'hide_customizer'} />
|
||||
) : (
|
||||
<T id={'customize_report'} />
|
||||
)
|
||||
}
|
||||
active={journalSheetFilter}
|
||||
onClick={handleFilterToggleClick}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Popover
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { Row, Col } from 'react-grid-system';
|
||||
import { Button } from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import { useFormik } from 'formik';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { Tab, Tabs, Button, Intent } from '@blueprintjs/core';
|
||||
import * as Yup from 'yup';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
import JournalSheetHeaderGeneralPanel from './JournalSheetHeaderGeneralPanel';
|
||||
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
|
||||
|
||||
import withJournal from './withJournal';
|
||||
@@ -23,49 +23,75 @@ function JournalHeader({
|
||||
|
||||
// #withJournalActions
|
||||
refreshJournalSheet,
|
||||
toggleJournalSheetFilter,
|
||||
|
||||
// #withJournal
|
||||
journalSheetFilter,
|
||||
journalSheetRefresh,
|
||||
}) {
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
...pageFilter,
|
||||
from_date: moment(pageFilter.from_date).toDate(),
|
||||
to_date: moment(pageFilter.to_date).toDate(),
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
from_date: Yup.date().required(),
|
||||
to_date: Yup.date().min(Yup.ref('from_date')).required(),
|
||||
}),
|
||||
onSubmit: (values, { setSubmitting }) => {
|
||||
onSubmitFilter(values);
|
||||
setSubmitting(false);
|
||||
},
|
||||
const initialValues = {
|
||||
...pageFilter,
|
||||
fromDate: moment(pageFilter.fromDate).toDate(),
|
||||
toDate: moment(pageFilter.toDate).toDate(),
|
||||
};
|
||||
|
||||
// Validation schema.
|
||||
const validationSchema = Yup.object().shape({
|
||||
fromDate: Yup.date().required(),
|
||||
toDate: Yup.date().min(Yup.ref('fromDate')).required(),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (journalSheetRefresh) {
|
||||
formik.submitForm();
|
||||
refreshJournalSheet(false);
|
||||
}
|
||||
}, [formik, journalSheetRefresh]);
|
||||
// Handle form submit.
|
||||
const handleSubmit = (values, { setSubmitting }) => {
|
||||
onSubmitFilter(values);
|
||||
setSubmitting(false);
|
||||
toggleJournalSheetFilter();
|
||||
};
|
||||
|
||||
// Handle cancel journal drawer header.
|
||||
const handleCancelClick = () => {
|
||||
toggleJournalSheetFilter();
|
||||
};
|
||||
|
||||
const handleDrawerClose = () => {
|
||||
toggleJournalSheetFilter();
|
||||
};
|
||||
|
||||
return (
|
||||
<FinancialStatementHeader show={journalSheetFilter}>
|
||||
<Row>
|
||||
<FinancialStatementDateRange formik={formik} />
|
||||
</Row>
|
||||
<FinancialStatementHeader
|
||||
isOpen={journalSheetFilter}
|
||||
drawerProps={{ onClose: handleDrawerClose }}
|
||||
>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleSubmit}
|
||||
validationSchema={validationSchema}
|
||||
>
|
||||
<Form>
|
||||
<Tabs animate={true} vertical={true} renderActiveTabPanelOnly={true}>
|
||||
<Tab
|
||||
id="general"
|
||||
title={'General'}
|
||||
panel={<JournalSheetHeaderGeneralPanel />}
|
||||
/>
|
||||
</Tabs>
|
||||
|
||||
<div class="financial-header-drawer__footer">
|
||||
<Button className={'mr1'} intent={Intent.PRIMARY} type={'submit'}>
|
||||
<T id={'calculate_report'} />
|
||||
</Button>
|
||||
<Button onClick={handleCancelClick} minimal={true}>
|
||||
<T id={'cancel'} />
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Formik>
|
||||
</FinancialStatementHeader>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withJournal(({
|
||||
journalSheetFilter,
|
||||
journalSheetRefresh
|
||||
}) => ({
|
||||
withJournal(({ journalSheetFilter, journalSheetRefresh }) => ({
|
||||
journalSheetFilter,
|
||||
journalSheetRefresh,
|
||||
})),
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
|
||||
export default function JournalSheetHeaderGeneralPanel({}) {
|
||||
return (
|
||||
<div>
|
||||
<FinancialStatementDateRange />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,17 +1,15 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import moment from 'moment';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import FinancialSheet from 'components/FinancialSheet';
|
||||
import DataTable from 'components/DataTable';
|
||||
import { compose, defaultExpanderReducer } from 'utils';
|
||||
|
||||
import Money from 'components/Money';
|
||||
import { getFinancialSheetIndexByQuery } from 'store/financialStatement/financialStatements.selectors';
|
||||
|
||||
import withJournal from './withJournal';
|
||||
|
||||
import { compose, defaultExpanderReducer } from 'utils';
|
||||
|
||||
function JournalSheetTable({
|
||||
// #withJournal
|
||||
journalSheetTableRows,
|
||||
@@ -106,12 +104,12 @@ function JournalSheetTable({
|
||||
return (
|
||||
<FinancialSheet
|
||||
companyName={companyName}
|
||||
// sheetType={formatMessage({ id: 'journal_sheet' })}
|
||||
sheetType={formatMessage({ id: 'journal_sheet' })}
|
||||
fromDate={journalSheetQuery.from_date}
|
||||
toDate={journalSheetQuery.to_date}
|
||||
name="journal"
|
||||
loading={journalSheetLoading}
|
||||
minimal={true}
|
||||
// minimal={true}
|
||||
fullWidth={true}
|
||||
>
|
||||
<DataTable
|
||||
@@ -129,20 +127,7 @@ function JournalSheetTable({
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const { journalQuery } = props;
|
||||
return {
|
||||
journalIndex: getFinancialSheetIndexByQuery(
|
||||
state.financialStatements.journal.sheets,
|
||||
journalQuery,
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const withJournalTable = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withJournalTable,
|
||||
withJournal(
|
||||
({ journalSheetTableRows, journalSheetLoading, journalSheetQuery }) => ({
|
||||
journalSheetTableRows,
|
||||
|
||||
@@ -1,34 +1,27 @@
|
||||
import {connect} from 'react-redux';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
getFinancialSheetIndexByQuery,
|
||||
getFinancialSheet,
|
||||
getFinancialSheetTableRows,
|
||||
getFinancialSheetQuery,
|
||||
getFinancialSheetFactory,
|
||||
getFinancialSheetTableRowsFactory,
|
||||
getFinancialSheetQueryFactory,
|
||||
} from 'store/financialStatement/financialStatements.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const mapStateToProps = (state, props) => {
|
||||
const { journalIndex } = props;
|
||||
const getJournalSheet = getFinancialSheetFactory('journal');
|
||||
const getJournalSheetTableRows = getFinancialSheetTableRowsFactory(
|
||||
'journal',
|
||||
);
|
||||
const getJournalSheetQuery = getFinancialSheetQueryFactory('journal');
|
||||
|
||||
const mapped = {
|
||||
journalSheet: getFinancialSheet(
|
||||
state.financialStatements.journal.sheets,
|
||||
journalIndex
|
||||
),
|
||||
journalSheetTableRows: getFinancialSheetTableRows(
|
||||
state.financialStatements.journal.sheets,
|
||||
journalIndex
|
||||
),
|
||||
journalSheetQuery: getFinancialSheetQuery(
|
||||
state.financialStatements.journal.sheets,
|
||||
journalIndex,
|
||||
),
|
||||
journalSheet: getJournalSheet(state, props),
|
||||
journalSheetTableRows: getJournalSheetTableRows(state, props),
|
||||
journalSheetQuery: getJournalSheetQuery(state, props),
|
||||
journalSheetLoading: state.financialStatements.journal.loading,
|
||||
journalSheetFilter: state.financialStatements.journal.filter,
|
||||
journalSheetRefresh: state.financialStatements.journal.refresh,
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
import React from 'react';
|
||||
import { NavbarGroup, Button, Classes, NavbarDivider } from '@blueprintjs/core';
|
||||
import {
|
||||
NavbarGroup,
|
||||
Button,
|
||||
Classes,
|
||||
NavbarDivider,
|
||||
Popover,
|
||||
Position,
|
||||
PopoverInteractionKind,
|
||||
} 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 { If } from 'components';
|
||||
import withProfitLossActions from './withProfitLossActions';
|
||||
import withProfitLoss from './withProfitLoss';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
|
||||
function ProfitLossActionsBar({
|
||||
// #withProfitLoss
|
||||
profitLossSheetFilter,
|
||||
@@ -33,45 +39,43 @@ function ProfitLossActionsBar({
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--table-views')}
|
||||
icon={<Icon icon="cog-16" iconSize={16} />}
|
||||
text={<T id={'customize_report'} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
className={classNames(
|
||||
Classes.MINIMAL,
|
||||
'button--gray-highlight',
|
||||
)}
|
||||
text={'Re-calc Report'}
|
||||
className={classNames(Classes.MINIMAL, 'button--gray-highlight')}
|
||||
text={<T id={'recalc_report'} />}
|
||||
onClick={handleRecalcReport}
|
||||
icon={<Icon icon="refresh-16" iconSize={16} />}
|
||||
/>
|
||||
|
||||
<If condition={profitLossSheetFilter}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={<T id={'hide_filter'} />}
|
||||
icon={<Icon icon="arrow-to-top" />}
|
||||
onClick={handleFilterClick}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<If condition={!profitLossSheetFilter}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={<T id={'show_filter'} />}
|
||||
icon={<Icon icon="arrow-to-bottom" />}
|
||||
onClick={handleFilterClick}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--table-views')}
|
||||
icon={<Icon icon="cog-16" iconSize={16} />}
|
||||
text={
|
||||
profitLossSheetFilter ? (
|
||||
<T id={'hide_customizer'} />
|
||||
) : (
|
||||
<T id={'customize_report'} />
|
||||
)
|
||||
}
|
||||
onClick={handleFilterClick}
|
||||
active={profitLossSheetFilter}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon='print-16' iconSize={16} />}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
text={<T id={'print'} />}
|
||||
/>
|
||||
<Button
|
||||
|
||||
@@ -2,6 +2,8 @@ import React, {useState, useCallback, useEffect} from 'react';
|
||||
import moment from 'moment';
|
||||
import {compose} from 'utils';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { queryCache } from 'react-query';
|
||||
|
||||
import ProfitLossSheetHeader from './ProfitLossSheetHeader';
|
||||
import ProfitLossSheetTable from './ProfitLossSheetTable';
|
||||
@@ -13,59 +15,71 @@ import DashboardPageContent from 'components/Dashboard/DashboardPageContent'
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withProfitLossActions from './withProfitLossActions';
|
||||
import withProfitLoss from './withProfitLoss';
|
||||
// import SettingsConnect from 'connectors/Settings.connect';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
|
||||
import { transformFilterFormToQuery } from 'containers/FinancialStatements/common';
|
||||
|
||||
function ProfitLossSheet({
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
setDashboardBackLink,
|
||||
|
||||
// #withProfitLoss
|
||||
profitLossSheetRefresh,
|
||||
|
||||
// #withProfitLossActions
|
||||
fetchProfitLossSheet,
|
||||
refreshProfitLossSheet,
|
||||
|
||||
// #withPreferences
|
||||
organizationSettings,
|
||||
organizationName,
|
||||
}) {
|
||||
const [filter, setFilter] = useState({
|
||||
basis: 'cash',
|
||||
from_date: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
to_date: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
displayColumnsType: 'total',
|
||||
accountsFilter: 'all-accounts',
|
||||
});
|
||||
const [refresh, setRefresh] = useState(true);
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
// Change page title of the dashboard.
|
||||
useEffect(() => {
|
||||
changePageTitle('Profit/Loss Sheet');
|
||||
}, [changePageTitle]);
|
||||
changePageTitle(formatMessage({ id: 'profit_loss_sheet' }));
|
||||
}, [changePageTitle, formatMessage]);
|
||||
|
||||
// Observes the P&L sheet refresh to invalid the query to refresh it.
|
||||
useEffect(() => {
|
||||
if (profitLossSheetRefresh) {
|
||||
refreshProfitLossSheet(false);
|
||||
queryCache.invalidateQueries('profit-loss-sheet');
|
||||
}
|
||||
}, [profitLossSheetRefresh, refreshProfitLossSheet]);
|
||||
|
||||
useEffect(() => {
|
||||
// Show the back link on dashboard topbar.
|
||||
setDashboardBackLink(true);
|
||||
|
||||
return () => {
|
||||
// Hide the back link on dashboard topbar.
|
||||
setDashboardBackLink(false);
|
||||
};
|
||||
});
|
||||
|
||||
// Fetches profit/loss sheet.
|
||||
const fetchHook = useQuery(['profit-loss', filter],
|
||||
(key, query) => fetchProfitLossSheet(query),
|
||||
const fetchSheetHook = useQuery(['profit-loss-sheet', filter],
|
||||
(key, query) => fetchProfitLossSheet({ ...transformFilterFormToQuery(query) }),
|
||||
{ manual: true });
|
||||
|
||||
// Handle submit filter.
|
||||
const handleSubmitFilter = useCallback((filter) => {
|
||||
const _filter = {
|
||||
...filter,
|
||||
from_date: moment(filter.from_date).format('YYYY-MM-DD'),
|
||||
to_date: moment(filter.to_date).format('YYYY-MM-DD'),
|
||||
fromDate: moment(filter.fromDate).format('YYYY-MM-DD'),
|
||||
toDate: moment(filter.toDate).format('YYYY-MM-DD'),
|
||||
};
|
||||
setFilter(_filter);
|
||||
setRefresh(true);
|
||||
}, []);
|
||||
|
||||
// Handle fetch data of profit/loss sheet table.
|
||||
const handleFetchData = useCallback(() => {
|
||||
setRefresh(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (refresh) {
|
||||
fetchHook.refetch({ force: true });
|
||||
setRefresh(false);
|
||||
}
|
||||
}, [refresh, fetchHook]);
|
||||
}, [setFilter]);
|
||||
|
||||
return (
|
||||
<DashboardInsider>
|
||||
@@ -79,10 +93,9 @@ function ProfitLossSheet({
|
||||
|
||||
<div class="financial-statement__body">
|
||||
<ProfitLossSheetTable
|
||||
companyName={organizationSettings.name}
|
||||
profitLossQuery={filter}
|
||||
onFetchData={handleFetchData} />
|
||||
</div>
|
||||
companyName={organizationName}
|
||||
profitLossQuery={filter} />
|
||||
</div>
|
||||
</div>
|
||||
</DashboardPageContent>
|
||||
</DashboardInsider>
|
||||
@@ -92,5 +105,8 @@ function ProfitLossSheet({
|
||||
export default compose(
|
||||
withDashboardActions,
|
||||
withProfitLossActions,
|
||||
withSettings,
|
||||
withProfitLoss(({ profitLossSheetRefresh }) => ({ profitLossSheetRefresh })),
|
||||
withSettings(({ organizationSettings }) => ({
|
||||
organizationName: organizationSettings.name,
|
||||
})),
|
||||
)(ProfitLossSheet);
|
||||
@@ -1,126 +1,102 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { Row, Col, Visible } from 'react-grid-system';
|
||||
import React, { useEffect } from 'react';
|
||||
import moment from 'moment';
|
||||
import { useFormik } from 'formik';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { FormGroup } from '@blueprintjs/core';
|
||||
import * as Yup from 'yup';
|
||||
import { Tabs, Tab, Button, Intent } from '@blueprintjs/core';
|
||||
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
|
||||
import SelectsListColumnsBy from '../SelectDisplayColumnsBy';
|
||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||
import FinancialAccountsFilter from '../FinancialAccountsFilter';
|
||||
import ProfitLossSheetHeaderGeneralPane from './ProfitLossSheetHeaderGeneralPane';
|
||||
|
||||
import withProfitLoss from './withProfitLoss';
|
||||
import withProfitLossActions from './withProfitLossActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
|
||||
function ProfitLossHeader({
|
||||
// #ownProps
|
||||
pageFilter,
|
||||
onSubmitFilter,
|
||||
|
||||
// #withProfitLoss
|
||||
profitLossSheetFilter,
|
||||
profitLossSheetRefresh,
|
||||
|
||||
// #withProfitLossActions
|
||||
refreshProfitLossSheet,
|
||||
toggleProfitLossSheetFilter,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
...pageFilter,
|
||||
from_date: moment(pageFilter.from_date).toDate(),
|
||||
to_date: moment(pageFilter.to_date).toDate(),
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
from_date: Yup.date()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'from_date' })),
|
||||
to_date: Yup.date()
|
||||
.min(Yup.ref('from_date'))
|
||||
.required()
|
||||
.label(formatMessage({ id: 'to_date' })),
|
||||
}),
|
||||
onSubmit: (values, actions) => {
|
||||
onSubmitFilter(values);
|
||||
actions.setSubmitting(false);
|
||||
},
|
||||
|
||||
// Validation schema.
|
||||
const validationSchema = Yup.object().shape({
|
||||
fromDate: Yup.date()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'from_date' })),
|
||||
toDate: Yup.date()
|
||||
.min(Yup.ref('fromDate'))
|
||||
.required()
|
||||
.label(formatMessage({ id: 'to_date' })),
|
||||
accountsFilter: Yup.string(),
|
||||
displayColumnsType: Yup.string(),
|
||||
});
|
||||
|
||||
// Handle item select of `display columns by` field.
|
||||
const handleItemSelectDisplayColumns = useCallback(
|
||||
(item) => {
|
||||
formik.setFieldValue('display_columns_type', item.type);
|
||||
formik.setFieldValue('display_columns_by', item.by);
|
||||
},
|
||||
[formik],
|
||||
);
|
||||
// Initial values.
|
||||
const initialValues = {
|
||||
...pageFilter,
|
||||
fromDate: moment(pageFilter.fromDate).toDate(),
|
||||
toDate: moment(pageFilter.toDate).toDate(),
|
||||
};
|
||||
|
||||
const handleAccountingBasisChange = useCallback(
|
||||
(value) => {
|
||||
formik.setFieldValue('basis', value);
|
||||
},
|
||||
[formik],
|
||||
);
|
||||
// Handle form submit.
|
||||
const handleSubmit = (values, actions) => {
|
||||
onSubmitFilter(values);
|
||||
toggleProfitLossSheetFilter();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (profitLossSheetRefresh) {
|
||||
formik.submitForm();
|
||||
refreshProfitLossSheet(false);
|
||||
}
|
||||
}, [profitLossSheetRefresh]);
|
||||
|
||||
const handleAccountsFilterSelect = (filterType) => {
|
||||
const noneZero = filterType.key === 'without-zero-balance' ? true : false;
|
||||
formik.setFieldValue('none_zero', noneZero);
|
||||
// Handles the cancel button click.
|
||||
const handleCancelClick = () => {
|
||||
toggleProfitLossSheetFilter();
|
||||
};
|
||||
// Handles the drawer close action.
|
||||
const handleDrawerClose = () => {
|
||||
toggleProfitLossSheetFilter();
|
||||
};
|
||||
|
||||
return (
|
||||
<FinancialStatementHeader show={profitLossSheetFilter}>
|
||||
<Row>
|
||||
<FinancialStatementDateRange formik={formik} />
|
||||
<Visible xl><Col width={'100%'} /></Visible>
|
||||
|
||||
<Col width={260}>
|
||||
<SelectsListColumnsBy onItemSelect={handleItemSelectDisplayColumns} />
|
||||
</Col>
|
||||
|
||||
<Col width={260}>
|
||||
<FormGroup
|
||||
label={<T id={'filter_accounts'} />}
|
||||
className="form-group--select-list bp3-fill"
|
||||
inline={false}
|
||||
>
|
||||
<FinancialAccountsFilter
|
||||
initialSelectedItem={'all-accounts'}
|
||||
onItemSelect={handleAccountsFilterSelect}
|
||||
<FinancialStatementHeader
|
||||
isOpen={profitLossSheetFilter}
|
||||
drawerProps={{ onClose: handleDrawerClose }}
|
||||
>
|
||||
<Formik
|
||||
validationSchema={validationSchema}
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Form>
|
||||
<Tabs animate={true} vertical={true} renderActiveTabPanelOnly={true}>
|
||||
<Tab
|
||||
id="general"
|
||||
title={<T id={'general'} />}
|
||||
panel={<ProfitLossSheetHeaderGeneralPane />}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Tabs>
|
||||
|
||||
<Col width={260}>
|
||||
<RadiosAccountingBasis
|
||||
selectedValue={formik.values.basis}
|
||||
onChange={handleAccountingBasisChange}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<div class="financial-header-drawer__footer">
|
||||
<Button className={'mr1'} intent={Intent.PRIMARY} type={'submit'}>
|
||||
<T id={'calculate_report'} />
|
||||
</Button>
|
||||
<Button onClick={handleCancelClick} minimal={true}>
|
||||
<T id={'cancel'} />
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Formik>
|
||||
</FinancialStatementHeader>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withProfitLoss(({
|
||||
withProfitLoss(({ profitLossSheetFilter }) => ({
|
||||
profitLossSheetFilter,
|
||||
profitLossSheetRefresh,
|
||||
}) => ({
|
||||
profitLossSheetFilter,
|
||||
profitLossSheetRefresh,
|
||||
})),
|
||||
withProfitLossActions,
|
||||
)(ProfitLossHeader);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy';
|
||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||
import FinancialAccountsFilter from '../FinancialAccountsFilter';
|
||||
|
||||
/**
|
||||
* Profit/Loss sheet - Drawer header - General panel.
|
||||
*/
|
||||
export default function ProfitLossSheetHeaderGeneralPane({}) {
|
||||
return (
|
||||
<div>
|
||||
<FinancialStatementDateRange />
|
||||
<SelectDisplayColumnsBy />
|
||||
<FinancialAccountsFilter initialSelectedItem={'all-accounts'} />
|
||||
<RadiosAccountingBasis key={'basis'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
|
||||
import FinancialSheet from 'components/FinancialSheet';
|
||||
import DataTable from 'components/DataTable';
|
||||
import Money from 'components/Money';
|
||||
|
||||
import { compose, defaultExpanderReducer } from 'utils';
|
||||
import { getFinancialSheetIndexByQuery } from 'store/financialStatement/financialStatements.selectors';
|
||||
import { compose, defaultExpanderReducer, getColumnWidth } from 'utils';
|
||||
import withProfitLossDetail from './withProfitLoss';
|
||||
|
||||
function ProfitLossSheetTable({
|
||||
@@ -18,7 +16,6 @@ function ProfitLossSheetTable({
|
||||
profitLossSheetLoading,
|
||||
|
||||
// #ownProps
|
||||
onFetchData,
|
||||
companyName,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
@@ -26,14 +23,10 @@ function ProfitLossSheetTable({
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: formatMessage({ id: 'account_name' }),
|
||||
accessor: 'name',
|
||||
Header: formatMessage({ id: 'account' }),
|
||||
accessor: (row) => (row.code ? `${row.name} - ${row.code}` : row.name),
|
||||
className: 'name',
|
||||
},
|
||||
{
|
||||
Header: formatMessage({ id: 'account_code' }),
|
||||
accessor: 'code',
|
||||
className: 'account_code',
|
||||
width: 240,
|
||||
},
|
||||
...(profitLossQuery.display_columns_type === 'total'
|
||||
? [
|
||||
@@ -45,13 +38,14 @@ function ProfitLossSheetTable({
|
||||
return (
|
||||
<Money
|
||||
amount={row.total.formatted_amount}
|
||||
currency={'USD'}
|
||||
currency={row.total.currency_code}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return '';
|
||||
},
|
||||
className: 'total',
|
||||
width: 140,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
@@ -60,40 +54,44 @@ function ProfitLossSheetTable({
|
||||
id: `date_period_${index}`,
|
||||
Header: column,
|
||||
accessor: (row) => {
|
||||
if (row.periods && row.periods[index]) {
|
||||
const amount = row.periods[index].formatted_amount;
|
||||
if (row.total_periods && row.total_periods[index]) {
|
||||
const amount = row.total_periods[index].formatted_amount;
|
||||
return <Money amount={amount} currency={'USD'} />;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
width: 100,
|
||||
width: getColumnWidth(
|
||||
profitLossTableRows,
|
||||
`total_periods.${index}.formatted_amount`,
|
||||
{ minWidth: 100 },
|
||||
),
|
||||
className: 'total-period',
|
||||
}))
|
||||
: []),
|
||||
],
|
||||
[profitLossQuery.display_columns_type, profitLossColumns, formatMessage],
|
||||
);
|
||||
|
||||
// Handle data table fetch data.
|
||||
const handleFetchData = useCallback(
|
||||
(...args) => {
|
||||
onFetchData && onFetchData(...args);
|
||||
},
|
||||
[onFetchData],
|
||||
[profitLossQuery.display_columns_type, profitLossTableRows, profitLossColumns, formatMessage],
|
||||
);
|
||||
|
||||
// Retrieve default expanded rows of balance sheet.
|
||||
const expandedRows = useMemo(
|
||||
() => defaultExpanderReducer(profitLossTableRows, 1),
|
||||
() => defaultExpanderReducer(profitLossTableRows, 3),
|
||||
[profitLossTableRows],
|
||||
);
|
||||
|
||||
// Retrieve conditional datatable row classnames.
|
||||
const rowClassNames = useCallback(
|
||||
(row) => ({
|
||||
[`row--${row.rowType}`]: row.rowType,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
const rowClassNames = useCallback((row) => {
|
||||
const { original } = row;
|
||||
const rowTypes = Array.isArray(original.rowTypes)
|
||||
? original.rowTypes
|
||||
: [];
|
||||
|
||||
return {
|
||||
...rowTypes.reduce((acc, rowType) => {
|
||||
acc[`row_type--${rowType}`] = rowType;
|
||||
return acc;
|
||||
}, {}),
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<FinancialSheet
|
||||
@@ -109,7 +107,6 @@ function ProfitLossSheetTable({
|
||||
className="bigcapital-datatable--financial-report"
|
||||
columns={columns}
|
||||
data={profitLossTableRows}
|
||||
onFetchData={handleFetchData}
|
||||
noInitialFetch={true}
|
||||
expanded={expandedRows}
|
||||
rowClassNames={rowClassNames}
|
||||
@@ -121,19 +118,14 @@ function ProfitLossSheetTable({
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
profitLossIndex: getFinancialSheetIndexByQuery(
|
||||
state.financialStatements.profitLoss.sheets,
|
||||
props.profitLossQuery,
|
||||
),
|
||||
});
|
||||
|
||||
const withProfitLossTable = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withProfitLossTable,
|
||||
withProfitLossDetail(
|
||||
({ profitLossQuery, profitLossColumns, profitLossTableRows, profitLossSheetLoading }) => ({
|
||||
({
|
||||
profitLossQuery,
|
||||
profitLossColumns,
|
||||
profitLossTableRows,
|
||||
profitLossSheetLoading,
|
||||
}) => ({
|
||||
profitLossColumns,
|
||||
profitLossQuery,
|
||||
profitLossTableRows,
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import {connect} from 'react-redux';
|
||||
import {
|
||||
getFinancialSheetIndexByQuery,
|
||||
getFinancialSheet,
|
||||
getFinancialSheetColumns,
|
||||
getFinancialSheetQuery,
|
||||
getFinancialSheetTableRows,
|
||||
getFinancialSheetFactory,
|
||||
getFinancialSheetColumnsFactory,
|
||||
getFinancialSheetQueryFactory,
|
||||
getFinancialSheetTableRowsFactory,
|
||||
} from 'store/financialStatement/financialStatements.selectors';
|
||||
|
||||
|
||||
export default (mapState) => {
|
||||
const mapStateToProps = (state, props) => {
|
||||
const { profitLossIndex } = props;
|
||||
const getProfitLossSheet = getFinancialSheetFactory('profitLoss');
|
||||
const getProfitLossColumns = getFinancialSheetColumnsFactory('profitLoss');
|
||||
const getProfitLossQuery = getFinancialSheetQueryFactory('profitLoss');
|
||||
const getProfitLossTableRows = getFinancialSheetTableRowsFactory('profitLoss');
|
||||
|
||||
const mapped = {
|
||||
profitLossSheet: getFinancialSheet(state.financialStatements.profitLoss.sheets, profitLossIndex),
|
||||
profitLossColumns: getFinancialSheetColumns(state.financialStatements.profitLoss.sheets, profitLossIndex),
|
||||
profitLossQuery: getFinancialSheetQuery(state.financialStatements.profitLoss.sheets, profitLossIndex),
|
||||
profitLossTableRows: getFinancialSheetTableRows(state.financialStatements.profitLoss.sheets, profitLossIndex),
|
||||
profitLossSheet: getProfitLossSheet(state, props),
|
||||
profitLossColumns: getProfitLossColumns(state, props),
|
||||
profitLossQuery: getProfitLossQuery(state, props),
|
||||
profitLossTableRows: getProfitLossTableRows(state, props),
|
||||
|
||||
profitLossSheetLoading: state.financialStatements.profitLoss.loading,
|
||||
profitLossSheetFilter: state.financialStatements.profitLoss.filter,
|
||||
|
||||
@@ -1,28 +1,34 @@
|
||||
import React from 'react';
|
||||
import {handleStringChange} from 'utils';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {
|
||||
RadioGroup,
|
||||
Radio,
|
||||
} from "@blueprintjs/core";
|
||||
|
||||
import { FastField } from 'formik';
|
||||
import { handleStringChange } from 'utils';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { RadioGroup, Radio } from '@blueprintjs/core';
|
||||
|
||||
export default function RadiosAccountingBasis(props) {
|
||||
const { onChange, ...rest } = props;
|
||||
const {formatMessage} = useIntl();
|
||||
const { key = 'basis', ...rest } = props;
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return (
|
||||
<RadioGroup
|
||||
inline={true}
|
||||
label={formatMessage({'id': 'accounting_basis'})}
|
||||
name="basis"
|
||||
onChange={handleStringChange((value) => {
|
||||
onChange && onChange(value);
|
||||
})}
|
||||
className={'radio-group---accounting-basis'}
|
||||
{...rest}>
|
||||
<Radio label={formatMessage({id:'cash'})} value="cash" />
|
||||
<Radio label={formatMessage({id:'accrual'})} value="accural" />
|
||||
</RadioGroup>
|
||||
<FastField name={'basis'}>
|
||||
{({
|
||||
form: { setFieldValue },
|
||||
field: { value },
|
||||
}) => (
|
||||
<RadioGroup
|
||||
inline={true}
|
||||
label={formatMessage({ id: 'accounting_basis' })}
|
||||
name="basis"
|
||||
onChange={handleStringChange((value) => {
|
||||
setFieldValue(key, value);
|
||||
})}
|
||||
className={'radio-group---accounting-basis'}
|
||||
selectedValue={value}
|
||||
{...rest}
|
||||
>
|
||||
<Radio label={formatMessage({ id: 'cash' })} value="cash" />
|
||||
<Radio label={formatMessage({ id: 'accrual' })} value="accural" />
|
||||
</RadioGroup>
|
||||
)}
|
||||
</FastField>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +1,43 @@
|
||||
|
||||
|
||||
import React, { useMemo, useState, useCallback } from 'react';
|
||||
import SelectList from 'components/SelectList';
|
||||
import {
|
||||
FormGroup,
|
||||
MenuItem,
|
||||
} from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
import { FormGroup } from '@blueprintjs/core';
|
||||
import { FastField } from 'formik';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { MODIFIER } from 'components';
|
||||
import { Row, Col, ListSelect } from 'components';
|
||||
import { displayColumnsByOptions } from 'containers/FinancialStatements/common';
|
||||
|
||||
/**
|
||||
* Financial statement - Display columns by and type select.
|
||||
*/
|
||||
export default function SelectsListColumnsBy(props) {
|
||||
const { onItemSelect, formGroupProps, selectListProps } = props;
|
||||
const [itemSelected, setItemSelected] = useState(null);
|
||||
|
||||
const displayColumnsByOptions = useMemo(() => [
|
||||
{key: 'total', name: 'Total', type: 'total', by: '', },
|
||||
{key: 'year', name: 'Date/Year', type: 'date_periods', by: 'year'},
|
||||
{key: 'month', name: 'Date/Month', type: 'date_periods', by: 'month'},
|
||||
{key: 'week', name: 'Date/Week', type: 'date_periods', by: 'month'},
|
||||
{key: 'day', name: 'Date/Day', type: 'date_periods', by: 'day'},
|
||||
{key: 'quarter', name: 'Date/Quarter', type: 'date_periods', by: 'quarter'},
|
||||
],[]);
|
||||
|
||||
const itemRenderer = useCallback((item, { handleClick, modifiers, query }) => {
|
||||
return (<MenuItem text={item.name} key={item.id} onClick={handleClick} />);
|
||||
}, []);
|
||||
|
||||
const handleItemSelect = useCallback((item) => {
|
||||
setItemSelected(item);
|
||||
onItemSelect && onItemSelect(item);
|
||||
}, [setItemSelected, onItemSelect]);
|
||||
|
||||
const buttonLabel = useMemo(() =>
|
||||
itemSelected ? itemSelected.name : <T id={'select_display_columns_by'}/>,
|
||||
[itemSelected]);
|
||||
const { formGroupProps, selectListProps } = props;
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
label={<T id={'display_report_columns'}/>}
|
||||
className="form-group-display-columns-by form-group--select-list bp3-fill"
|
||||
inline={false}
|
||||
{...formGroupProps}>
|
||||
|
||||
<SelectList
|
||||
items={displayColumnsByOptions}
|
||||
noResults={<MenuItem disabled={true} text="No results." />}
|
||||
filterable={false}
|
||||
itemRenderer={itemRenderer}
|
||||
popoverProps={{ minimal: true, usePortal: false, inline: true }}
|
||||
buttonLabel={buttonLabel}
|
||||
onItemSelect={handleItemSelect}
|
||||
className={classNames(MODIFIER.SELECT_LIST_FILL_POPOVER)}
|
||||
{...selectListProps} />
|
||||
</FormGroup>
|
||||
<Row>
|
||||
<Col xs={4}>
|
||||
<FastField name={'displayColumnsType'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'display_report_columns'} />}
|
||||
className="form-group-display-columns-by form-group--select-list bp3-fill"
|
||||
inline={false}
|
||||
{...formGroupProps}
|
||||
>
|
||||
<ListSelect
|
||||
items={displayColumnsByOptions}
|
||||
filterable={false}
|
||||
selectedItem={value}
|
||||
selectedItemProp={'key'}
|
||||
labelProp={'name'}
|
||||
onItemSelect={(item) => {
|
||||
form.setFieldValue('displayColumnsType', item.key);
|
||||
}}
|
||||
popoverProps={{ minimal: true }}
|
||||
{...selectListProps}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
import React from 'react';
|
||||
import { NavbarGroup, Button, Classes, NavbarDivider } from '@blueprintjs/core';
|
||||
import Icon from 'components/Icon';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
import {
|
||||
NavbarGroup,
|
||||
Button,
|
||||
Classes,
|
||||
NavbarDivider,
|
||||
Popover,
|
||||
PopoverInteractionKind,
|
||||
Position,
|
||||
} from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
// import FilterDropdown from 'components/FilterDropdown';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
import { If } from 'components';
|
||||
import Icon from 'components/Icon';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
|
||||
import withTrialBalance from './withTrialBalance';
|
||||
import withTrialBalanceActions from './withTrialBalanceActions';
|
||||
import { compose } from 'utils';
|
||||
|
||||
|
||||
function TrialBalanceActionsBar({
|
||||
|
||||
// #withTrialBalance
|
||||
trialBalanceSheetFilter,
|
||||
|
||||
@@ -22,7 +26,6 @@ function TrialBalanceActionsBar({
|
||||
toggleTrialBalanceFilter,
|
||||
refreshTrialBalance,
|
||||
}) {
|
||||
|
||||
const handleFilterToggleClick = () => {
|
||||
toggleTrialBalanceFilter();
|
||||
};
|
||||
@@ -35,45 +38,43 @@ function TrialBalanceActionsBar({
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--table-views')}
|
||||
icon={<Icon icon="cog-16" iconSize={16} />}
|
||||
text={<T id={'customize_report'} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
className={classNames(
|
||||
Classes.MINIMAL,
|
||||
'button--gray-highlight',
|
||||
)}
|
||||
className={classNames(Classes.MINIMAL, 'button--gray-highlight')}
|
||||
text={'Re-calc Report'}
|
||||
onClick={handleRecalcReport}
|
||||
icon={<Icon icon="refresh-16" iconSize={16} />}
|
||||
/>
|
||||
|
||||
<If condition={trialBalanceSheetFilter}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={<T id={'hide_filter'} />}
|
||||
icon={<Icon icon="arrow-to-top" />}
|
||||
onClick={handleFilterToggleClick}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<If condition={!trialBalanceSheetFilter}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={<T id={'show_filter'} />}
|
||||
icon={<Icon icon="arrow-to-bottom" />}
|
||||
onClick={handleFilterToggleClick}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--table-views')}
|
||||
icon={<Icon icon="cog-16" iconSize={16} />}
|
||||
text={
|
||||
trialBalanceSheetFilter ? (
|
||||
<T id={'hide_customizer'} />
|
||||
) : (
|
||||
<T id={'customize_report'} />
|
||||
)
|
||||
}
|
||||
active={trialBalanceSheetFilter}
|
||||
onClick={handleFilterToggleClick}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon='print-16' iconSize={16} />}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
text={<T id={'print'} />}
|
||||
/>
|
||||
<Button
|
||||
@@ -87,6 +88,8 @@ function TrialBalanceActionsBar({
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withTrialBalance(({ trialBalanceSheetFilter }) => ({ trialBalanceSheetFilter })),
|
||||
withTrialBalanceActions
|
||||
)(TrialBalanceActionsBar);
|
||||
withTrialBalance(({ trialBalanceSheetFilter }) => ({
|
||||
trialBalanceSheetFilter,
|
||||
})),
|
||||
withTrialBalanceActions,
|
||||
)(TrialBalanceActionsBar);
|
||||
|
||||
@@ -2,73 +2,93 @@ import React, { useEffect, useCallback, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import moment from 'moment';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { queryCache } from 'react-query';
|
||||
|
||||
import TrialBalanceActionsBar from './TrialBalanceActionsBar';
|
||||
import TrialBalanceSheetHeader from './TrialBalanceSheetHeader';
|
||||
import TrialBalanceSheetTable from './TrialBalanceSheetTable';
|
||||
import TrialBalanceActionsBar from './TrialBalanceActionsBar';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { transformFilterFormToQuery } from 'containers/FinancialStatements/common';
|
||||
|
||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withTrialBalanceActions from './withTrialBalanceActions';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withTrialBalance from './withTrialBalance';
|
||||
|
||||
/**
|
||||
* Trial balance sheet.
|
||||
*/
|
||||
function TrialBalanceSheet({
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
setDashboardBackLink,
|
||||
|
||||
// #withTrialBalance
|
||||
trialBalanceSheetRefresh,
|
||||
|
||||
// #withTrialBalanceActions
|
||||
fetchTrialBalanceSheet,
|
||||
refreshTrialBalance,
|
||||
|
||||
// #withPreferences
|
||||
organizationSettings,
|
||||
organizationName,
|
||||
}) {
|
||||
const [filter, setFilter] = useState({
|
||||
from_date: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
to_date: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
basis: 'accural',
|
||||
none_zero: false,
|
||||
});
|
||||
const [refresh, setRefresh] = useState(true);
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const fetchHook = useQuery(
|
||||
['trial-balance', filter],
|
||||
(key, query) => fetchTrialBalanceSheet(query),
|
||||
const [filter, setFilter] = useState({
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
basis: 'accural',
|
||||
accountsFilter: 'all-accounts',
|
||||
});
|
||||
|
||||
// Fetches trial balance sheet.
|
||||
const fetchSheet = useQuery(
|
||||
['trial-balance-sheet', filter],
|
||||
(key, query) =>
|
||||
fetchTrialBalanceSheet({
|
||||
...transformFilterFormToQuery(query),
|
||||
}),
|
||||
{ manual: true },
|
||||
);
|
||||
|
||||
// handle fetch data of trial balance table.
|
||||
const handleFetchData = useCallback(() => {
|
||||
setRefresh(true);
|
||||
}, []);
|
||||
|
||||
// Change page title of the dashboard.
|
||||
useEffect(() => {
|
||||
changePageTitle(formatMessage({ id: 'trial_balance_sheet' }));
|
||||
}, [changePageTitle, formatMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
// Show the back link on dashboard topbar.
|
||||
setDashboardBackLink(true);
|
||||
|
||||
return () => {
|
||||
// Hide the back link on dashboard topbar.
|
||||
setDashboardBackLink(false);
|
||||
};
|
||||
});
|
||||
|
||||
const handleFilterSubmit = useCallback(
|
||||
(filter) => {
|
||||
const parsedFilter = {
|
||||
...filter,
|
||||
from_date: moment(filter.from_date).format('YYYY-MM-DD'),
|
||||
to_date: moment(filter.to_date).format('YYYY-MM-DD'),
|
||||
fromDate: moment(filter.fromDate).format('YYYY-MM-DD'),
|
||||
toDate: moment(filter.toDate).format('YYYY-MM-DD'),
|
||||
};
|
||||
setFilter(parsedFilter);
|
||||
setRefresh(true);
|
||||
refreshTrialBalance(true);
|
||||
},
|
||||
[fetchHook],
|
||||
[setFilter, refreshTrialBalance],
|
||||
);
|
||||
|
||||
// Observes the trial balance sheet refresh to invaoid the query.
|
||||
useEffect(() => {
|
||||
if (refresh) {
|
||||
fetchHook.refetch({ force: true });
|
||||
setRefresh(false);
|
||||
if (trialBalanceSheetRefresh) {
|
||||
queryCache.invalidateQueries('trial-balance-sheet');
|
||||
refreshTrialBalance(false);
|
||||
}
|
||||
}, [refresh, fetchHook.refetch]);
|
||||
}, [trialBalanceSheetRefresh, refreshTrialBalance]);
|
||||
|
||||
return (
|
||||
<DashboardInsider>
|
||||
@@ -82,11 +102,7 @@ function TrialBalanceSheet({
|
||||
/>
|
||||
|
||||
<div class="financial-statement__body">
|
||||
<TrialBalanceSheetTable
|
||||
companyName={organizationSettings.name}
|
||||
trialBalanceQuery={filter}
|
||||
onFetchData={handleFetchData}
|
||||
/>
|
||||
<TrialBalanceSheetTable companyName={organizationName} />
|
||||
</div>
|
||||
</div>
|
||||
</DashboardPageContent>
|
||||
@@ -97,5 +113,10 @@ function TrialBalanceSheet({
|
||||
export default compose(
|
||||
withDashboardActions,
|
||||
withTrialBalanceActions,
|
||||
withSettings,
|
||||
withTrialBalance(({ trialBalanceSheetRefresh }) => ({
|
||||
trialBalanceSheetRefresh,
|
||||
})),
|
||||
withSettings(({ organizationSettings }) => ({
|
||||
organizationName: organizationSettings.name,
|
||||
})),
|
||||
)(TrialBalanceSheet);
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import moment from 'moment';
|
||||
import { Row, Col, Visible } from 'react-grid-system';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { FormGroup } from '@blueprintjs/core';
|
||||
import { useFormik } from 'formik';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { Tabs, Tab, Button, Intent } from '@blueprintjs/core';
|
||||
|
||||
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||
import FinancialAccountsFilter from '../FinancialAccountsFilter';
|
||||
import TrialBalanceSheetHeaderGeneralPanel from './TrialBalanceSheetHeaderGeneralPanel';
|
||||
|
||||
import withTrialBalance from './withTrialBalance';
|
||||
import withTrialBalanceActions from './withTrialBalanceActions';
|
||||
@@ -17,6 +14,7 @@ import withTrialBalanceActions from './withTrialBalanceActions';
|
||||
import { compose } from 'utils';
|
||||
|
||||
function TrialBalanceSheetHeader({
|
||||
// #ownProps
|
||||
pageFilter,
|
||||
onSubmitFilter,
|
||||
|
||||
@@ -26,78 +24,78 @@ function TrialBalanceSheetHeader({
|
||||
|
||||
// #withTrialBalanceActions
|
||||
refreshTrialBalance,
|
||||
toggleTrialBalanceFilter
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
...pageFilter,
|
||||
from_date: moment(pageFilter.from_date).toDate(),
|
||||
to_date: moment(pageFilter.to_date).toDate(),
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
from_date: Yup.date()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'from_date' })),
|
||||
to_date: Yup.date()
|
||||
.min(Yup.ref('from_date'))
|
||||
.required()
|
||||
.label(formatMessage({ id: 'to_date' })),
|
||||
}),
|
||||
onSubmit: (values, { setSubmitting }) => {
|
||||
onSubmitFilter(values);
|
||||
setSubmitting(false);
|
||||
},
|
||||
|
||||
// Form validation schema.
|
||||
const validationSchema = Yup.object().shape({
|
||||
fromDate: Yup.date()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'from_date' })),
|
||||
toDate: Yup.date()
|
||||
.min(Yup.ref('fromDate'))
|
||||
.required()
|
||||
.label(formatMessage({ id: 'to_date' })),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (trialBalanceSheetRefresh) {
|
||||
formik.submitForm();
|
||||
refreshTrialBalance(false);
|
||||
}
|
||||
}, [formik, trialBalanceSheetRefresh]);
|
||||
// Initial values.
|
||||
const initialValues = {
|
||||
...pageFilter,
|
||||
fromDate: moment(pageFilter.fromDate).toDate(),
|
||||
toDate: moment(pageFilter.toDate).toDate(),
|
||||
};
|
||||
|
||||
const handleAccountingBasisChange = useCallback(
|
||||
(value) => {
|
||||
formik.setFieldValue('basis', value);
|
||||
},
|
||||
[formik],
|
||||
);
|
||||
// Handle form submit.
|
||||
const handleSubmit = (values, { setSubmitting }) => {
|
||||
onSubmitFilter(values);
|
||||
setSubmitting(false);
|
||||
toggleTrialBalanceFilter(false);
|
||||
};
|
||||
|
||||
const handleAccountsFilterSelect = (filterType) => {
|
||||
const noneZero = filterType.key === 'without-zero-balance' ? true : false;
|
||||
formik.setFieldValue('none_zero', noneZero);
|
||||
// Handle drawer close action.
|
||||
const handleDrawerClose = () => {
|
||||
toggleTrialBalanceFilter(false);
|
||||
};
|
||||
|
||||
// Handle cancel button click.
|
||||
const handleCancelClick = () => {
|
||||
toggleTrialBalanceFilter(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<FinancialStatementHeader show={trialBalanceSheetFilter}>
|
||||
<Row>
|
||||
<FinancialStatementDateRange formik={formik} />
|
||||
|
||||
<Visible xl>
|
||||
<Col width={'100%'} />
|
||||
</Visible>
|
||||
|
||||
<Col width={260}>
|
||||
<FormGroup
|
||||
label={<T id={'filter_accounts'} />}
|
||||
className="form-group--select-list bp3-fill"
|
||||
inline={false}
|
||||
>
|
||||
<FinancialAccountsFilter
|
||||
initialSelectedItem={'all-accounts'}
|
||||
onItemSelect={handleAccountsFilterSelect}
|
||||
<FinancialStatementHeader
|
||||
isOpen={trialBalanceSheetFilter}
|
||||
drawerProps={{ onClose: handleDrawerClose }}
|
||||
>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Form>
|
||||
<Tabs animate={true} vertical={true} renderActiveTabPanelOnly={true}>
|
||||
<Tab
|
||||
id="general"
|
||||
title={<T id={'general'} />}
|
||||
panel={<TrialBalanceSheetHeaderGeneralPanel />}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Tabs>
|
||||
|
||||
<Col width={260}>
|
||||
<RadiosAccountingBasis
|
||||
selectedValue={formik.values.basis}
|
||||
onChange={handleAccountingBasisChange}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<div class="financial-header-drawer__footer">
|
||||
<Button
|
||||
className={'mr1'}
|
||||
intent={Intent.PRIMARY}
|
||||
type={'submit'}
|
||||
>
|
||||
<T id={'calculate_report'} />
|
||||
</Button>
|
||||
<Button onClick={handleCancelClick} minimal={true}>
|
||||
<T id={'cancel'} />
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Formik>
|
||||
</FinancialStatementHeader>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||
import FinancialAccountsFilter from '../FinancialAccountsFilter';
|
||||
|
||||
/**
|
||||
* Trial balance sheet - Drawer header - General panel.
|
||||
*/
|
||||
export default function TrialBalanceSheetHeaderGeneralPanel({
|
||||
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<FinancialStatementDateRange />
|
||||
<FinancialAccountsFilter initialSelectedItem={'all-accounts'} />
|
||||
<RadiosAccountingBasis />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import FinancialSheet from 'components/FinancialSheet';
|
||||
import DataTable from 'components/DataTable';
|
||||
import Money from 'components/Money';
|
||||
import { getFinancialSheetIndexByQuery } from 'store/financialStatement/financialStatements.selectors';
|
||||
|
||||
import withTrialBalance from './withTrialBalance';
|
||||
|
||||
@@ -13,14 +11,12 @@ import { compose } from 'utils';
|
||||
|
||||
function TrialBalanceSheetTable({
|
||||
// #withTrialBalanceDetail
|
||||
trialBalanceAccounts,
|
||||
trialBalance,
|
||||
trialBalanceSheetLoading,
|
||||
|
||||
// #withTrialBalanceTable
|
||||
trialBalanceIndex,
|
||||
trialBalanceQuery,
|
||||
|
||||
onFetchData,
|
||||
companyName,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
@@ -29,55 +25,46 @@ function TrialBalanceSheetTable({
|
||||
() => [
|
||||
{
|
||||
Header: formatMessage({ id: 'account_name' }),
|
||||
accessor: 'name',
|
||||
accessor: (row) => (row.code ? `${row.name} - ${row.code}` : row.name),
|
||||
className: 'name',
|
||||
minWidth: 150,
|
||||
maxWidth: 150,
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
Header: formatMessage({ id: 'code' }),
|
||||
accessor: 'code',
|
||||
className: 'code',
|
||||
minWidth: 80,
|
||||
maxWidth: 80,
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
Header: formatMessage({ id: 'credit' }),
|
||||
accessor: 'credit',
|
||||
Cell: ({ cell }) => <Money amount={cell.row.original.credit} currency="USD" />,
|
||||
Cell: ({ cell }) => {
|
||||
const { currency_code, credit } = cell.row.original;
|
||||
return (<Money amount={credit} currency={currency_code} />);
|
||||
},
|
||||
className: 'credit',
|
||||
minWidth: 95,
|
||||
maxWidth: 95,
|
||||
width: 95,
|
||||
},
|
||||
{
|
||||
Header: formatMessage({ id: 'debit' }),
|
||||
accessor: 'debit',
|
||||
Cell: ({ cell }) => <Money amount={cell.row.original.debit} currency="USD" />,
|
||||
Cell: ({ cell }) => {
|
||||
const { currency_code, debit } = cell.row.original;
|
||||
return (<Money amount={debit} currency={currency_code} />);
|
||||
},
|
||||
className: 'debit',
|
||||
minWidth: 95,
|
||||
maxWidth: 95,
|
||||
width: 95,
|
||||
},
|
||||
{
|
||||
Header: formatMessage({ id: 'balance' }),
|
||||
accessor: 'balance',
|
||||
Cell: ({ cell }) => <Money amount={cell.row.original.balance} currency="USD" />,
|
||||
Cell: ({ cell }) => {
|
||||
const { currency_code, balance } = cell.row.original;
|
||||
return (<Money amount={balance} currency={currency_code} />);
|
||||
},
|
||||
className: 'balance',
|
||||
minWidth: 95,
|
||||
maxWidth: 95,
|
||||
width: 95,
|
||||
},
|
||||
],
|
||||
[formatMessage],
|
||||
);
|
||||
|
||||
const handleFetchData = useCallback(() => {
|
||||
onFetchData && onFetchData();
|
||||
}, [onFetchData]);
|
||||
|
||||
return (
|
||||
<FinancialSheet
|
||||
companyName={companyName}
|
||||
@@ -86,12 +73,12 @@ function TrialBalanceSheetTable({
|
||||
toDate={trialBalanceQuery.to_date}
|
||||
name="trial-balance"
|
||||
loading={trialBalanceSheetLoading}
|
||||
basis={'cash'}
|
||||
>
|
||||
<DataTable
|
||||
className="bigcapital-datatable--financial-report"
|
||||
columns={columns}
|
||||
data={trialBalanceAccounts}
|
||||
onFetchData={handleFetchData}
|
||||
data={trialBalance.data}
|
||||
expandable={true}
|
||||
expandToggleColumn={1}
|
||||
expandColumnSpace={1}
|
||||
@@ -101,25 +88,14 @@ function TrialBalanceSheetTable({
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const { trialBalanceQuery } = props;
|
||||
return {
|
||||
trialBalanceIndex: getFinancialSheetIndexByQuery(
|
||||
state.financialStatements.trialBalance.sheets,
|
||||
trialBalanceQuery,
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const withTrialBalanceTable = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withTrialBalanceTable,
|
||||
withTrialBalance(({
|
||||
trialBalanceAccounts,
|
||||
trialBalance,
|
||||
trialBalanceSheetLoading,
|
||||
trialBalanceQuery
|
||||
}) => ({
|
||||
trialBalanceAccounts,
|
||||
trialBalanceSheetLoading
|
||||
trialBalance,
|
||||
trialBalanceSheetLoading,
|
||||
trialBalanceQuery
|
||||
})),
|
||||
)(TrialBalanceSheetTable);
|
||||
|
||||
@@ -1,28 +1,22 @@
|
||||
import {connect} from 'react-redux';
|
||||
import {
|
||||
getFinancialSheetAccounts,
|
||||
getFinancialSheetQuery,
|
||||
getFinancialSheetFactory,
|
||||
getFinancialSheetQueryFactory,
|
||||
} from 'store/financialStatement/financialStatements.selectors';
|
||||
|
||||
|
||||
export default (mapState) => {
|
||||
const mapStateToProps = (state, props) => {
|
||||
const { trialBalanceIndex } = props;
|
||||
const getTrialBalance = getFinancialSheetFactory('trialBalance');
|
||||
const getBalanceSheetQuery = getFinancialSheetQueryFactory('trialBalance');
|
||||
|
||||
const mapped = {
|
||||
trialBalanceAccounts: getFinancialSheetAccounts(
|
||||
state.financialStatements.trialBalance.sheets,
|
||||
trialBalanceIndex
|
||||
),
|
||||
trialBalanceQuery: getFinancialSheetQuery(
|
||||
state.financialStatements.trialBalance.sheets,
|
||||
trialBalanceIndex
|
||||
),
|
||||
trialBalance: getTrialBalance(state, props),
|
||||
trialBalanceQuery: getBalanceSheetQuery(state, props),
|
||||
trialBalanceSheetLoading: state.financialStatements.trialBalance.loading,
|
||||
trialBalanceSheetFilter: state.financialStatements.trialBalance.filter,
|
||||
trialBalanceSheetRefresh: state.financialStatements.trialBalance.refresh,
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
|
||||
61
client/src/containers/FinancialStatements/common.js
Normal file
61
client/src/containers/FinancialStatements/common.js
Normal file
@@ -0,0 +1,61 @@
|
||||
import { mapKeys, omit, snakeCase } from 'lodash';
|
||||
import { formatMessage } from 'services/intl';
|
||||
|
||||
export const displayColumnsByOptions = [
|
||||
{ key: 'total', name: 'Total', type: 'total', by: '' },
|
||||
{ key: 'year', name: 'Date/Year', type: 'date_periods', by: 'year' },
|
||||
{ key: 'month', name: 'Date/Month', type: 'date_periods', by: 'month' },
|
||||
{ key: 'week', name: 'Date/Week', type: 'date_periods', by: 'month' },
|
||||
{ key: 'day', name: 'Date/Day', type: 'date_periods', by: 'day' },
|
||||
{ key: 'quarter', name: 'Date/Quarter', type: 'date_periods', by: 'quarter' },
|
||||
];
|
||||
|
||||
export const dateRangeOptions = [
|
||||
{ value: 'today', label: 'Today' },
|
||||
{ value: 'this_week', label: 'This Week' },
|
||||
{ value: 'this_month', label: 'This Month' },
|
||||
{ value: 'this_quarter', label: 'This Quarter' },
|
||||
{ value: 'this_year', label: 'This Year' },
|
||||
{ value: 'custom', label: 'Custom Range' },
|
||||
];
|
||||
|
||||
export const filterAccountsOptions = [
|
||||
{
|
||||
key: 'all-accounts',
|
||||
name: formatMessage({ id: 'all_accounts' }),
|
||||
hint: formatMessage({ id: 'all_accounts_including_with_zero_balance' }),
|
||||
},
|
||||
{
|
||||
key: 'without-zero-balance',
|
||||
name: formatMessage({ id: 'accounts_without_zero_balance' }),
|
||||
hint: formatMessage({
|
||||
id: 'include_accounts_and_exclude_zero_balance',
|
||||
}),
|
||||
},
|
||||
{
|
||||
key: 'with-transactions',
|
||||
name: formatMessage({ id: 'accounts_with_transactions' }),
|
||||
hint: formatMessage({
|
||||
id: 'include_accounts_once_has_transactions_on_given_date_period',
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
export const transformDisplayColumnsType = (form) => {
|
||||
const columnType = displayColumnsByOptions.find(
|
||||
(o) => o.key === form.displayColumnsType,
|
||||
);
|
||||
return {
|
||||
displayColumnsBy: columnType ? columnType.by : '',
|
||||
displayColumnsType: columnType ? columnType.type : 'total',
|
||||
};
|
||||
};
|
||||
|
||||
export const transformFilterFormToQuery = (form) => {
|
||||
return mapKeys({
|
||||
...omit(form, ['accountsFilter']),
|
||||
...transformDisplayColumnsType(form),
|
||||
noneZero: form.accountsFilter === 'without-zero-balance',
|
||||
noneTransactions: form.accountsFilter === 'with-transactions',
|
||||
}, (v, k) => snakeCase(k));
|
||||
};
|
||||
Reference in New Issue
Block a user