mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
Merge branch 'master' of https://github.com/abouolia/Bigcapital
This commit is contained in:
@@ -49,7 +49,6 @@ const CLASSES = {
|
||||
|
||||
SELECT_LIST_FILL_POPOVER: 'select-list--fill-popover',
|
||||
|
||||
|
||||
PREFERENCES_PAGE: 'preferences-page',
|
||||
PREFERENCES_PAGE_SIDEBAR: 'preferences-page__sidebar',
|
||||
PREFERENCES_PAGE_TOPBAR: 'preferences-page__topbar',
|
||||
|
||||
@@ -104,6 +104,7 @@ export default function DataTable({
|
||||
initialState: {
|
||||
pageIndex: initialPageIndex,
|
||||
pageSize: initialPageSize,
|
||||
expanded
|
||||
},
|
||||
manualPagination,
|
||||
pageCount: controlledPageCount,
|
||||
|
||||
@@ -41,6 +41,7 @@ import EmptyStatus from './EmptyStatus';
|
||||
import DashboardCard from './Dashboard/DashboardCard';
|
||||
import InputPrependText from './Forms/InputPrependText';
|
||||
import PageFormBigNumber from './PageFormBigNumber';
|
||||
import AccountsMultiSelect from './AccountsMultiSelect';
|
||||
|
||||
const Hint = FieldHint;
|
||||
|
||||
@@ -87,5 +88,6 @@ export {
|
||||
EmptyStatus,
|
||||
DashboardCard,
|
||||
InputPrependText,
|
||||
PageFormBigNumber
|
||||
PageFormBigNumber,
|
||||
AccountsMultiSelect,
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
};
|
||||
@@ -9,6 +9,14 @@ import * as serviceWorker from 'serviceWorker';
|
||||
import { store, persistor } from 'store/createStore';
|
||||
import AppProgress from 'components/NProgress/AppProgress';
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
const whyDidYouRender = require('@welldone-software/why-did-you-render');
|
||||
|
||||
whyDidYouRender(React, {
|
||||
trackAllPureComponents: false,
|
||||
});
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
|
||||
@@ -256,7 +256,7 @@ export default {
|
||||
accrual: 'Accrual',
|
||||
from: 'From',
|
||||
to: 'To',
|
||||
accounting_basis: 'Accounting Basis:',
|
||||
accounting_basis: 'Accounting basis:',
|
||||
general: 'General',
|
||||
users: 'Users',
|
||||
currencies: 'Currencies',
|
||||
@@ -282,7 +282,7 @@ export default {
|
||||
journal: 'Journal',
|
||||
general_ledger: 'General Ledger',
|
||||
general_ledger_sheet: 'General Ledger Sheet',
|
||||
profit_loss_sheet: 'Profit Loss Sheet',
|
||||
profit_loss_sheet: 'Profit/Loss Sheet',
|
||||
expenses: 'Expenses',
|
||||
expenses_list: 'Expenses List',
|
||||
new_expenses: 'New Expenses',
|
||||
@@ -335,8 +335,8 @@ export default {
|
||||
export: 'Export',
|
||||
accounts_with_zero_balance: 'Accounts with Zero Balance',
|
||||
all_transactions: 'All Transactions',
|
||||
filter_accounts: 'Filter Accounts',
|
||||
calculate_report: 'Calculate Report',
|
||||
filter_accounts: 'Filter accounts',
|
||||
calculate_report: 'Calculate report',
|
||||
total: 'Total',
|
||||
specific_accounts: 'Specific Accounts',
|
||||
trans_num: 'Trans. NUM',
|
||||
@@ -929,7 +929,8 @@ export default {
|
||||
'Are you sure you want to activate this item? You will be able to inactivate it later',
|
||||
inactivate_item: 'Inactivate Item',
|
||||
activate_item: 'Activate Item',
|
||||
all_payments: 'All Payments',
|
||||
all_payments:'All Payments',
|
||||
hide_customizer: 'Hide Customizer',
|
||||
opening_quantity_: 'Opening quantity',
|
||||
opening_average_cost: 'Opening average cost',
|
||||
opening_cost_: 'Opening cost ',
|
||||
|
||||
@@ -112,7 +112,7 @@ export default [
|
||||
component: LazyLoader({
|
||||
loader: () =>
|
||||
import(
|
||||
'containers/FinancialStatements/TrialBalanceSheet/TrialBalanceSheet'
|
||||
'containers/FinancialStatements/TrialBalanceSheet/TrialBalanceSheet'
|
||||
),
|
||||
}),
|
||||
breadcrumb: 'Trial Balance Sheet',
|
||||
@@ -127,16 +127,16 @@ export default [
|
||||
}),
|
||||
breadcrumb: 'Profit Loss Sheet',
|
||||
},
|
||||
{
|
||||
path: '/financial-reports/receivable-aging-summary',
|
||||
component: LazyLoader({
|
||||
loader: () =>
|
||||
import(
|
||||
'containers/FinancialStatements/ReceivableAgingSummary/ReceivableAgingSummary'
|
||||
),
|
||||
}),
|
||||
breadcrumb: 'Receivable Aging Summary',
|
||||
},
|
||||
// {
|
||||
// path: '/financial-reports/receivable-aging-summary',
|
||||
// component: LazyLoader({
|
||||
// loader: () =>
|
||||
// import(
|
||||
// 'containers/FinancialStatements/ReceivableAgingSummary/ReceivableAgingSummary'
|
||||
// ),
|
||||
// }),
|
||||
// breadcrumb: 'Receivable Aging Summary',
|
||||
// },
|
||||
{
|
||||
path: `/financial-reports/journal-sheet`,
|
||||
component: LazyLoader({
|
||||
|
||||
@@ -95,7 +95,7 @@ export const fetchProfitLossSheet = ({ query }) => {
|
||||
ApiService.get('/financial_statements/profit_loss_sheet', { params: query }).then((response) => {
|
||||
dispatch({
|
||||
type: t.PROFIT_LOSS_SHEET_SET,
|
||||
profitLoss: response.data.profitLoss,
|
||||
profitLoss: response.data.data,
|
||||
columns: response.data.columns,
|
||||
query: response.data.query,
|
||||
});
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
import { omit } from 'lodash';
|
||||
|
||||
export const mapBalanceSheetToTableRows = (accounts) => {
|
||||
return accounts.map((account) => {
|
||||
const PRIMARY_SECTIONS = ['assets', 'liability', 'equity'];
|
||||
const rowTypes = [
|
||||
'total_row',
|
||||
...(PRIMARY_SECTIONS.indexOf(account.section_type) !== -1
|
||||
? ['total_assets']
|
||||
: []),
|
||||
];
|
||||
return {
|
||||
...account,
|
||||
children: mapBalanceSheetToTableRows([
|
||||
...(account.children ? account.children : []),
|
||||
...(account.total && account.children && account.children.length > 0
|
||||
? [
|
||||
{
|
||||
name: `Total ${account.name}`,
|
||||
row_types: rowTypes,
|
||||
total: { ...account.total },
|
||||
...(account.total_periods && {
|
||||
total_periods: account.total_periods,
|
||||
}),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const journalToTableRowsMapper = (journal) => {
|
||||
return journal.reduce((rows, journal) => {
|
||||
journal.entries.forEach((entry, index) => {
|
||||
rows.push({
|
||||
...entry,
|
||||
rowType: index === 0 ? 'first_entry' : 'entry',
|
||||
});
|
||||
});
|
||||
rows.push({
|
||||
credit: journal.credit,
|
||||
debit: journal.debit,
|
||||
rowType: 'entries_total',
|
||||
});
|
||||
rows.push({
|
||||
rowType: 'space_entry',
|
||||
});
|
||||
return rows;
|
||||
}, []);
|
||||
};
|
||||
|
||||
|
||||
export const generalLedgerToTableRows = (accounts) => {
|
||||
return accounts.reduce((tableRows, account) => {
|
||||
const children = [];
|
||||
children.push({
|
||||
...account.opening,
|
||||
rowType: 'opening_balance',
|
||||
});
|
||||
account.transactions.map((transaction) => {
|
||||
children.push({
|
||||
...transaction,
|
||||
...omit(account, ['transactions']),
|
||||
rowType: 'transaction',
|
||||
});
|
||||
});
|
||||
children.push({
|
||||
...account.closing,
|
||||
rowType: 'closing_balance',
|
||||
});
|
||||
tableRows.push({
|
||||
...omit(account, ['transactions']),
|
||||
children,
|
||||
rowType: 'account_name',
|
||||
});
|
||||
return tableRows;
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const profitLossToTableRowsMapper = (profitLoss) => {
|
||||
|
||||
return [
|
||||
{
|
||||
name: 'Income',
|
||||
total: profitLoss.income.total,
|
||||
children: [
|
||||
...profitLoss.income.accounts,
|
||||
{
|
||||
name: 'Total Income',
|
||||
total: profitLoss.income.total,
|
||||
total_periods: profitLoss.income.total_periods,
|
||||
rowTypes: ['income_total', 'section_total', 'total'],
|
||||
},
|
||||
],
|
||||
total_periods: profitLoss.income.total_periods,
|
||||
},
|
||||
{
|
||||
name: 'Cost of sales',
|
||||
total: profitLoss.cost_of_sales.total,
|
||||
children: [
|
||||
...profitLoss.cost_of_sales.accounts,
|
||||
{
|
||||
name: 'Total cost of sales',
|
||||
total: profitLoss.cost_of_sales.total,
|
||||
total_periods: profitLoss.cost_of_sales.total_periods,
|
||||
rowTypes: ['cogs_total', 'section_total', 'total'],
|
||||
},
|
||||
],
|
||||
total_periods: profitLoss.cost_of_sales.total_periods
|
||||
},
|
||||
{
|
||||
name: 'Gross profit',
|
||||
total: profitLoss.gross_profit.total,
|
||||
total_periods: profitLoss.gross_profit.total_periods,
|
||||
rowTypes: ['gross_total', 'section_total', 'total'],
|
||||
},
|
||||
{
|
||||
name: 'Expenses',
|
||||
total: profitLoss.expenses.total,
|
||||
children: [
|
||||
...profitLoss.expenses.accounts,
|
||||
{
|
||||
name: 'Total Expenses',
|
||||
total: profitLoss.expenses.total,
|
||||
total_periods: profitLoss.expenses.total_periods,
|
||||
rowTypes: ['expenses_total', 'section_total', 'total'],
|
||||
},
|
||||
],
|
||||
total_periods: profitLoss.expenses.total_periods,
|
||||
},
|
||||
{
|
||||
name: 'Net Operating income',
|
||||
total: profitLoss.operating_profit.total,
|
||||
total_periods: profitLoss.income.total_periods,
|
||||
rowTypes: ['net_operating_total', 'section_total', 'total'],
|
||||
},
|
||||
{
|
||||
name: 'Other expenses',
|
||||
total: profitLoss.other_expenses.total,
|
||||
total_periods: profitLoss.other_expenses.total_periods,
|
||||
children: [
|
||||
...profitLoss.other_expenses.accounts,
|
||||
{
|
||||
name: 'Total other expenses',
|
||||
total: profitLoss.other_expenses.total,
|
||||
total_periods: profitLoss.other_expenses.total_periods,
|
||||
rowTypes: ['expenses_total', 'section_total', 'total'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Net Income',
|
||||
total: profitLoss.net_income.total,
|
||||
total_periods: profitLoss.net_income.total_periods,
|
||||
rowTypes: ['net_income_total', 'section_total', 'total'],
|
||||
},
|
||||
];
|
||||
};
|
||||
@@ -1,36 +1,41 @@
|
||||
import { createReducer } from '@reduxjs/toolkit';
|
||||
import t from 'store/types';
|
||||
import { getFinancialSheetIndexByQuery } from './financialStatements.selectors';
|
||||
import { omit } from 'lodash';
|
||||
import {
|
||||
mapBalanceSheetToTableRows,
|
||||
journalToTableRowsMapper,
|
||||
generalLedgerToTableRows,
|
||||
profitLossToTableRowsMapper
|
||||
} from './financialStatements.mappers';
|
||||
|
||||
const initialState = {
|
||||
balanceSheet: {
|
||||
sheets: [],
|
||||
sheet: {},
|
||||
loading: true,
|
||||
filter: true,
|
||||
refresh: false,
|
||||
},
|
||||
trialBalance: {
|
||||
sheets: [],
|
||||
sheet: {},
|
||||
loading: true,
|
||||
filter: true,
|
||||
refresh: false,
|
||||
},
|
||||
generalLedger: {
|
||||
sheets: [],
|
||||
sheet: {},
|
||||
loading: false,
|
||||
filter: true,
|
||||
refresh: false,
|
||||
},
|
||||
journal: {
|
||||
sheets: [],
|
||||
sheet: {},
|
||||
loading: false,
|
||||
tableRows: [],
|
||||
filter: true,
|
||||
refresh: true,
|
||||
},
|
||||
profitLoss: {
|
||||
sheets: [],
|
||||
sheet: {},
|
||||
loading: true,
|
||||
tableRows: [],
|
||||
filter: true,
|
||||
@@ -44,52 +49,8 @@ const initialState = {
|
||||
},
|
||||
};
|
||||
|
||||
const mapGeneralLedgerAccountsToRows = (accounts) => {
|
||||
return accounts.reduce((tableRows, account) => {
|
||||
const children = [];
|
||||
children.push({
|
||||
...account.opening,
|
||||
rowType: 'opening_balance',
|
||||
});
|
||||
account.transactions.map((transaction) => {
|
||||
children.push({
|
||||
...transaction,
|
||||
...omit(account, ['transactions']),
|
||||
rowType: 'transaction',
|
||||
});
|
||||
});
|
||||
children.push({
|
||||
...account.closing,
|
||||
rowType: 'closing_balance',
|
||||
});
|
||||
tableRows.push({
|
||||
...omit(account, ['transactions']),
|
||||
children,
|
||||
rowType: 'account_name',
|
||||
});
|
||||
return tableRows;
|
||||
}, []);
|
||||
};
|
||||
|
||||
const mapJournalTableRows = (journal) => {
|
||||
return journal.reduce((rows, journal) => {
|
||||
journal.entries.forEach((entry, index) => {
|
||||
rows.push({
|
||||
...entry,
|
||||
rowType: index === 0 ? 'first_entry' : 'entry',
|
||||
});
|
||||
});
|
||||
rows.push({
|
||||
credit: journal.credit,
|
||||
debit: journal.debit,
|
||||
rowType: 'entries_total',
|
||||
});
|
||||
rows.push({
|
||||
rowType: 'space_entry',
|
||||
});
|
||||
return rows;
|
||||
}, []);
|
||||
};
|
||||
|
||||
|
||||
const mapContactAgingSummary = (sheet) => {
|
||||
const rows = [];
|
||||
@@ -120,70 +81,6 @@ const mapContactAgingSummary = (sheet) => {
|
||||
return rows;
|
||||
};
|
||||
|
||||
const mapProfitLossToTableRows = (profitLoss) => {
|
||||
return [
|
||||
{
|
||||
name: 'Income',
|
||||
total: profitLoss.income.total,
|
||||
children: [
|
||||
...profitLoss.income.accounts,
|
||||
{
|
||||
name: 'Total Income',
|
||||
total: profitLoss.income.total,
|
||||
rowType: 'income_total',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Expenses',
|
||||
total: profitLoss.expenses.total,
|
||||
children: [
|
||||
...profitLoss.expenses.accounts,
|
||||
{
|
||||
name: 'Total Expenses',
|
||||
total: profitLoss.expenses.total,
|
||||
rowType: 'expense_total',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Net Income',
|
||||
total: profitLoss.net_income.total,
|
||||
rowType: 'net_income',
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const mapTotalToChildrenRows = (accounts) => {
|
||||
return accounts.map((account) => {
|
||||
return {
|
||||
...account,
|
||||
children: mapTotalToChildrenRows([
|
||||
...(account.children ? account.children : []),
|
||||
...(account.total &&
|
||||
account.children &&
|
||||
account.children.length > 0 &&
|
||||
account.row_type !== 'total_row'
|
||||
? [
|
||||
{
|
||||
name: `Total ${account.name}`,
|
||||
row_type: 'total_row',
|
||||
total: { ...account.total },
|
||||
...(account.total_periods && {
|
||||
total_periods: account.total_periods,
|
||||
}),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const mapBalanceSheetRows = (balanceSheet) => {
|
||||
return balanceSheet.map((section) => {});
|
||||
};
|
||||
|
||||
const financialStatementFilterToggle = (financialName, statePath) => {
|
||||
return {
|
||||
[`${financialName}_FILTER_TOGGLE`]: (state, action) => {
|
||||
@@ -194,22 +91,13 @@ const financialStatementFilterToggle = (financialName, statePath) => {
|
||||
|
||||
export default createReducer(initialState, {
|
||||
[t.BALANCE_SHEET_STATEMENT_SET]: (state, action) => {
|
||||
const index = getFinancialSheetIndexByQuery(
|
||||
state.balanceSheet.sheets,
|
||||
action.query,
|
||||
);
|
||||
|
||||
const balanceSheet = {
|
||||
sheet: action.data.balanceSheet,
|
||||
columns: Object.values(action.data.columns),
|
||||
sheet: action.data.data,
|
||||
columns: action.data.columns,
|
||||
query: action.data.query,
|
||||
tableRows: mapTotalToChildrenRows(action.data.balance_sheet),
|
||||
tableRows: mapBalanceSheetToTableRows(action.data.data),
|
||||
};
|
||||
if (index !== -1) {
|
||||
state.balanceSheet.sheets[index] = balanceSheet;
|
||||
} else {
|
||||
state.balanceSheet.sheets.push(balanceSheet);
|
||||
}
|
||||
state.balanceSheet.sheet = balanceSheet;
|
||||
},
|
||||
|
||||
[t.BALANCE_SHEET_LOADING]: (state, action) => {
|
||||
@@ -224,19 +112,11 @@ export default createReducer(initialState, {
|
||||
...financialStatementFilterToggle('BALANCE_SHEET', 'balanceSheet'),
|
||||
|
||||
[t.TRAIL_BALANCE_STATEMENT_SET]: (state, action) => {
|
||||
const index = getFinancialSheetIndexByQuery(
|
||||
state.trialBalance.sheets,
|
||||
action.query,
|
||||
);
|
||||
const trailBalanceSheet = {
|
||||
accounts: action.data.accounts,
|
||||
data: action.data.data,
|
||||
query: action.data.query,
|
||||
};
|
||||
if (index !== -1) {
|
||||
state.trialBalance.sheets[index] = trailBalanceSheet;
|
||||
} else {
|
||||
state.trialBalance.sheets.push(trailBalanceSheet);
|
||||
}
|
||||
state.trialBalance.sheet = trailBalanceSheet;
|
||||
},
|
||||
|
||||
[t.TRIAL_BALANCE_SHEET_LOADING]: (state, action) => {
|
||||
@@ -251,21 +131,12 @@ export default createReducer(initialState, {
|
||||
...financialStatementFilterToggle('TRIAL_BALANCE', 'trialBalance'),
|
||||
|
||||
[t.JOURNAL_SHEET_SET]: (state, action) => {
|
||||
const index = getFinancialSheetIndexByQuery(
|
||||
state.journal.sheets,
|
||||
action.query,
|
||||
);
|
||||
|
||||
const journal = {
|
||||
query: action.data.query,
|
||||
journal: action.data.journal,
|
||||
tableRows: mapJournalTableRows(action.data.journal),
|
||||
data: action.data.data,
|
||||
tableRows: journalToTableRowsMapper(action.data.data),
|
||||
};
|
||||
if (index !== -1) {
|
||||
state.journal.sheets[index] = journal;
|
||||
} else {
|
||||
state.journal.sheets.push(journal);
|
||||
}
|
||||
state.journal.sheet = journal;
|
||||
},
|
||||
|
||||
[t.JOURNAL_SHEET_LOADING]: (state, action) => {
|
||||
@@ -278,21 +149,12 @@ export default createReducer(initialState, {
|
||||
...financialStatementFilterToggle('JOURNAL', 'journal'),
|
||||
|
||||
[t.GENERAL_LEDGER_STATEMENT_SET]: (state, action) => {
|
||||
const index = getFinancialSheetIndexByQuery(
|
||||
state.generalLedger.sheets,
|
||||
action.query,
|
||||
);
|
||||
|
||||
const generalLedger = {
|
||||
query: action.data.query,
|
||||
accounts: action.data.accounts,
|
||||
tableRows: mapGeneralLedgerAccountsToRows(action.data.accounts),
|
||||
accounts: action.data.data,
|
||||
tableRows: generalLedgerToTableRows(action.data.data),
|
||||
};
|
||||
if (index !== -1) {
|
||||
state.generalLedger.sheets[index] = generalLedger;
|
||||
} else {
|
||||
state.generalLedger.sheets.push(generalLedger);
|
||||
}
|
||||
state.generalLedger.sheet = generalLedger;
|
||||
},
|
||||
|
||||
[t.GENERAL_LEDGER_SHEET_LOADING]: (state, action) => {
|
||||
@@ -305,22 +167,13 @@ export default createReducer(initialState, {
|
||||
...financialStatementFilterToggle('GENERAL_LEDGER', 'generalLedger'),
|
||||
|
||||
[t.PROFIT_LOSS_SHEET_SET]: (state, action) => {
|
||||
const index = getFinancialSheetIndexByQuery(
|
||||
state.profitLoss.sheets,
|
||||
action.query,
|
||||
);
|
||||
|
||||
const profitLossSheet = {
|
||||
query: action.query,
|
||||
profitLoss: action.profitLoss,
|
||||
columns: action.columns,
|
||||
tableRows: mapProfitLossToTableRows(action.profitLoss),
|
||||
tableRows: profitLossToTableRowsMapper(action.profitLoss),
|
||||
};
|
||||
if (index !== -1) {
|
||||
state.profitLoss.sheets[index] = profitLossSheet;
|
||||
} else {
|
||||
state.profitLoss.sheets.push(profitLossSheet);
|
||||
}
|
||||
state.profitLoss.sheet = profitLossSheet;
|
||||
},
|
||||
|
||||
[t.PROFIT_LOSS_SHEET_LOADING]: (state, action) => {
|
||||
@@ -334,34 +187,34 @@ export default createReducer(initialState, {
|
||||
|
||||
...financialStatementFilterToggle('PROFIT_LOSS', 'profitLoss'),
|
||||
|
||||
[t.RECEIVABLE_AGING_SUMMARY_LOADING]: (state, action) => {
|
||||
const { loading } = action.payload;
|
||||
state.receivableAgingSummary.loading = loading;
|
||||
},
|
||||
// [t.RECEIVABLE_AGING_SUMMARY_LOADING]: (state, action) => {
|
||||
// const { loading } = action.payload;
|
||||
// state.receivableAgingSummary.loading = loading;
|
||||
// },
|
||||
|
||||
[t.RECEIVABLE_AGING_SUMMARY_SET]: (state, action) => {
|
||||
const { aging, columns, query } = action.payload;
|
||||
const index = getFinancialSheetIndexByQuery(
|
||||
state.receivableAgingSummary.sheets,
|
||||
query,
|
||||
);
|
||||
// [t.RECEIVABLE_AGING_SUMMARY_SET]: (state, action) => {
|
||||
// const { aging, columns, query } = action.payload;
|
||||
// const index = getFinancialSheetIndexByQuery(
|
||||
// state.receivableAgingSummary.sheets,
|
||||
// query,
|
||||
// );
|
||||
|
||||
const receivableSheet = {
|
||||
query,
|
||||
columns,
|
||||
aging,
|
||||
tableRows: mapContactAgingSummary(aging),
|
||||
};
|
||||
if (index !== -1) {
|
||||
state.receivableAgingSummary[index] = receivableSheet;
|
||||
} else {
|
||||
state.receivableAgingSummary.sheets.push(receivableSheet);
|
||||
}
|
||||
},
|
||||
[t.RECEIVABLE_AGING_SUMMARY_REFRESH]: (state, action) => {
|
||||
const { refresh } = action.payload;
|
||||
state.receivableAgingSummary.refresh = !!refresh;
|
||||
},
|
||||
// const receivableSheet = {
|
||||
// query,
|
||||
// columns,
|
||||
// aging,
|
||||
// tableRows: mapContactAgingSummary(aging),
|
||||
// };
|
||||
// if (index !== -1) {
|
||||
// state.receivableAgingSummary[index] = receivableSheet;
|
||||
// } else {
|
||||
// state.receivableAgingSummary.sheets.push(receivableSheet);
|
||||
// }
|
||||
// },
|
||||
// [t.RECEIVABLE_AGING_SUMMARY_REFRESH]: (state, action) => {
|
||||
// const { refresh } = action.payload;
|
||||
// state.receivableAgingSummary.refresh = !!refresh;
|
||||
// },
|
||||
...financialStatementFilterToggle(
|
||||
'RECEIVABLE_AGING_SUMMARY',
|
||||
'receivableAgingSummary',
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import {getObjectDiff} from 'utils';
|
||||
import { createSelector } from 'reselect';
|
||||
import { camelCase } from 'lodash';
|
||||
|
||||
const transformSheetType = (sheetType) => {
|
||||
return camelCase(sheetType);
|
||||
};
|
||||
|
||||
// Financial Statements selectors.
|
||||
|
||||
/**
|
||||
* Retrieve financial statement sheet by the given query.
|
||||
* @param {array} sheets
|
||||
* @param {object} query
|
||||
*/
|
||||
export const getFinancialSheetIndexByQuery = (sheets, query) => {
|
||||
return sheets.findIndex(balanceSheet => (
|
||||
getObjectDiff(query, balanceSheet.query).length === 0
|
||||
));
|
||||
export const sheetByTypeSelector = (sheetType) => (state, props) => {
|
||||
const sheetName = transformSheetType(sheetType);
|
||||
return state.financialStatements[sheetName].sheet;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -19,38 +16,56 @@ export const getFinancialSheetIndexByQuery = (sheets, query) => {
|
||||
* @param {array} sheets
|
||||
* @param {number} index
|
||||
*/
|
||||
export const getFinancialSheet = (sheets, index) => {
|
||||
return (typeof sheets[index] !== 'undefined') ? sheets[index] : null;
|
||||
};
|
||||
export const getFinancialSheetFactory = (sheetType) =>
|
||||
createSelector(
|
||||
sheetByTypeSelector(sheetType),
|
||||
(sheet) => {
|
||||
return sheet;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieve financial statement columns by the given sheet index.
|
||||
* @param {array} sheets
|
||||
* @param {number} index
|
||||
*/
|
||||
export const getFinancialSheetColumns = (sheets, index) => {
|
||||
const sheet = getFinancialSheet(sheets, index);
|
||||
return (sheet && sheet.columns) ? sheet.columns : [];
|
||||
};
|
||||
export const getFinancialSheetColumnsFactory = (sheetType) =>
|
||||
createSelector(
|
||||
sheetByTypeSelector(sheetType),
|
||||
(sheet) => {
|
||||
return (sheet && sheet.columns) ? sheet.columns : [];
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieve financial statement query by the given sheet index.
|
||||
* @param {array} sheets
|
||||
* @param {number} index
|
||||
*/
|
||||
export const getFinancialSheetQuery = (sheets, index) => {
|
||||
const sheet = getFinancialSheet(sheets, index);
|
||||
return (sheet && sheet.query) ? sheet.query : {};
|
||||
};
|
||||
export const getFinancialSheetQueryFactory = (sheetType) =>
|
||||
createSelector(
|
||||
sheetByTypeSelector(sheetType),
|
||||
(sheet) => {
|
||||
return (sheet && sheet.query) ? sheet.query : {};
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieve financial statement accounts by the given sheet index.
|
||||
*/
|
||||
export const getFinancialSheetAccountsFactory = (sheetType) =>
|
||||
createSelector(
|
||||
sheetByTypeSelector(sheetType),
|
||||
(sheet) => {
|
||||
return (sheet && sheet.accounts) ? sheet.accounts : [];
|
||||
}
|
||||
);
|
||||
|
||||
export const getFinancialSheetAccounts = (sheets, index) => {
|
||||
const sheet = getFinancialSheet(sheets, index);
|
||||
return (sheet && sheet.accounts) ? sheet.accounts : [];
|
||||
};
|
||||
|
||||
|
||||
export const getFinancialSheetTableRows = (sheets, index) => {
|
||||
const sheet = getFinancialSheet(sheets, index);
|
||||
return (sheet && sheet.tableRows) ? sheet.tableRows : [];
|
||||
};
|
||||
/**
|
||||
* Retrieve financial statement table rows by the given sheet index.
|
||||
*/
|
||||
export const getFinancialSheetTableRowsFactory = (sheetType) =>
|
||||
createSelector(
|
||||
sheetByTypeSelector(sheetType),
|
||||
(sheet) => {
|
||||
return (sheet && sheet.tableRows) ? sheet.tableRows : [];
|
||||
}
|
||||
);
|
||||
|
||||
@@ -117,6 +117,9 @@ $button-background-color-hover: #CFDCEE !default;
|
||||
body.authentication {
|
||||
background-color: #fcfdff;
|
||||
}
|
||||
body.hide-scrollbar .Pane2{
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bp3-toast {
|
||||
box-shadow: none;
|
||||
|
||||
@@ -296,6 +296,12 @@
|
||||
|
||||
.tbody{
|
||||
.tr .td{
|
||||
border-bottom: 0;
|
||||
}
|
||||
.tr:not(:first-child) .td{
|
||||
border-top: 1px dotted #CCC;
|
||||
}
|
||||
.tr:last-child .td{
|
||||
border-bottom: 1px dotted #CCC;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,32 +6,7 @@
|
||||
.financial-statement{
|
||||
|
||||
&__header{
|
||||
padding: 25px 26px 25px;
|
||||
background: #FDFDFD;
|
||||
|
||||
&.is-hidden{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bp3-form-group,
|
||||
.radio-group---accounting-basis{
|
||||
|
||||
.bp3-label{
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
color: #444;
|
||||
}
|
||||
}
|
||||
.bp3-button.button--submit-filter{
|
||||
min-height: 34px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
.radio-group---accounting-basis{
|
||||
.bp3-label{
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__body{
|
||||
@@ -41,25 +16,146 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
&__header.is-hidden + .financial-statement__body{
|
||||
.financial-sheet{
|
||||
margin-top: 40px;
|
||||
.financial-header-drawer{
|
||||
padding: 25px 26px 25px;
|
||||
position: absolute;
|
||||
top: 101px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow: hidden;
|
||||
|
||||
&.is-hidden{
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.row{
|
||||
.col{
|
||||
max-width: 400px;
|
||||
min-width: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-drawer{
|
||||
box-shadow: 0 0 0 transparent;
|
||||
max-height: 550px;
|
||||
height: 100%;
|
||||
padding-bottom: 49px;
|
||||
|
||||
> form{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 0 0;
|
||||
height: 100%;
|
||||
}
|
||||
.bp3-drawer-backdrop{
|
||||
background-color: rgba(2, 9, 19, 0.65);
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-form-group{
|
||||
margin-bottom: 22px;
|
||||
|
||||
label.bp3-label{
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-button.button--submit-filter{
|
||||
min-height: 34px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
.radio-group---accounting-basis{
|
||||
.bp3-label{
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-tabs{
|
||||
height: 100%;
|
||||
|
||||
&.bp3-vertical > .bp3-tab-panel{
|
||||
flex: 1 0 0;
|
||||
border-top: 24px solid transparent;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
padding-bottom: 24px;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-tabs.bp3-vertical{
|
||||
flex: 1 0 0;
|
||||
|
||||
.bp3-tab-list{
|
||||
width: 220px;
|
||||
border-right: 1px solid #c3cdd5;
|
||||
padding-top: 10px;
|
||||
|
||||
> *:not(:last-child){
|
||||
margin-right: 0;
|
||||
}
|
||||
.bp3-tab-indicator-wrapper{
|
||||
width: 100%;
|
||||
|
||||
.bp3-tab-indicator{
|
||||
border-left: 3px solid #0350f8;
|
||||
background-color: #edf5ff;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-tab{
|
||||
color: #333;
|
||||
line-height: 45px;
|
||||
border-radius: 0;
|
||||
padding-left: 14px;
|
||||
padding-right: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-tab-panel{
|
||||
|
||||
}
|
||||
|
||||
&__footer{
|
||||
background-color: #ecf0f3;
|
||||
border-top: 1px solid #c3cdd5;
|
||||
padding: 8px;
|
||||
padding-left: 230px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.row{
|
||||
margin-left: -0.85rem;
|
||||
margin-right: -0.85rem;
|
||||
|
||||
.col{
|
||||
padding-left: 0.85rem;
|
||||
padding-right: 0.85rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.financial-sheet{
|
||||
border: 2px solid #F1F1F1;
|
||||
border: 2px solid #EBEBEB;
|
||||
border-radius: 10px;
|
||||
min-width: 640px;
|
||||
width: auto;
|
||||
padding: 30px 18px;
|
||||
max-width: 100%;
|
||||
margin: 15px auto 35px;
|
||||
margin: 35px auto;
|
||||
min-height: 400px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
|
||||
&__title{
|
||||
margin: 0;
|
||||
@@ -103,7 +199,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__inner{
|
||||
&.is-loading{
|
||||
display: none;
|
||||
@@ -113,8 +208,8 @@
|
||||
color: #888;
|
||||
text-align: center;
|
||||
margin-top: auto;
|
||||
padding-top: 16px;
|
||||
font-size: 12px;
|
||||
padding-top: 18px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.dashboard__loading-indicator{
|
||||
margin: auto;
|
||||
@@ -137,13 +232,19 @@
|
||||
}
|
||||
&--general-ledger{
|
||||
.financial-sheet__table{
|
||||
.tbody{
|
||||
.tbody{
|
||||
.tr.row-type{
|
||||
|
||||
&--opening_balance,
|
||||
&--closing_balance{
|
||||
.td{
|
||||
background-color: #fbfbfb;
|
||||
border-top: 1px solid #333;
|
||||
}
|
||||
|
||||
.name,
|
||||
.amount,
|
||||
.balance{
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,18 +286,36 @@
|
||||
&--profit-loss-sheet{
|
||||
|
||||
.financial-sheet__table{
|
||||
.thead,
|
||||
.tbody{
|
||||
.total.td {
|
||||
border-bottom-color: #000;
|
||||
.tr .td:not(:first-child),
|
||||
.tr .th:not(:first-child) {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
.tbody{
|
||||
.tr .td:not(:first-child) {
|
||||
border-top-color: #000;
|
||||
}
|
||||
.tr.row_type--total{
|
||||
font-weight: 500;
|
||||
}
|
||||
.tr.row_type--section_total .td{
|
||||
border-top: 1px solid #BBB
|
||||
}
|
||||
.tr.row_type--section_total + .tr .td{
|
||||
border-top: 1px solid #666;
|
||||
}
|
||||
.tr.row_type--section_total:last-child .td{
|
||||
border-bottom: 1px solid #666;
|
||||
}
|
||||
|
||||
.row--income_total,
|
||||
.row--expense_total,
|
||||
.row--net_income{
|
||||
font-weight: 600;
|
||||
|
||||
.total.td{
|
||||
border-bottom-color: #555;
|
||||
.tr.is-expanded{
|
||||
.td.total,
|
||||
.td.total-period{
|
||||
> span{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,13 +324,28 @@
|
||||
|
||||
&--balance-sheet{
|
||||
.financial-sheet__table{
|
||||
|
||||
.thead,
|
||||
.tbody{
|
||||
.total.td{
|
||||
border-bottom-color: #000;
|
||||
.tr .td.account_name ~ .td,
|
||||
.tr .th.account_name ~ .th{
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
.tbody{
|
||||
.tr .total.td{
|
||||
border-top-color: #000;
|
||||
}
|
||||
.tr.row_type--total_row .td{
|
||||
border-top: 1px solid #BBB;
|
||||
}
|
||||
.tr.row_type--total_assets + .tr .td{
|
||||
border-top: 1px solid #666;
|
||||
}
|
||||
.tr.row_type--total_row{
|
||||
.total.td,
|
||||
.account_name.td{
|
||||
.account_name.td,
|
||||
.total-period.td{
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
@@ -267,6 +401,7 @@
|
||||
|
||||
&.is-full-width{
|
||||
width: 100%;
|
||||
margin-top: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,4 +444,14 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.financial-statement--journal{
|
||||
|
||||
|
||||
.financial-header-drawer{
|
||||
.bp3-drawer{
|
||||
max-height: 350px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,24 +122,22 @@ export const parseDateRangeQuery = (keyword) => {
|
||||
const query = queries[keyword];
|
||||
|
||||
return {
|
||||
from_date: moment().startOf(query.range).toDate(),
|
||||
to_date: moment().endOf(query.range).toDate(),
|
||||
fromDate: moment().startOf(query.range).toDate(),
|
||||
toDate: moment().endOf(query.range).toDate(),
|
||||
};
|
||||
};
|
||||
|
||||
export const defaultExpanderReducer = (tableRows, level) => {
|
||||
let currentLevel = 1;
|
||||
const expended = [];
|
||||
|
||||
const walker = (rows, parentIndex = null) => {
|
||||
const walker = (rows, parentIndex = null, currentLevel = 1) => {
|
||||
return rows.forEach((row, index) => {
|
||||
const _index = parentIndex ? `${parentIndex}.${index}` : `${index}`;
|
||||
expended[_index] = true;
|
||||
|
||||
if (row.children && currentLevel < level) {
|
||||
walker(row.children, _index);
|
||||
walker(row.children, _index, currentLevel + 1);
|
||||
}
|
||||
currentLevel++;
|
||||
}, {});
|
||||
};
|
||||
walker(tableRows);
|
||||
@@ -371,4 +369,22 @@ export function defaultToTransform(
|
||||
|
||||
export function isBlank(value) {
|
||||
return _.isEmpty(value) && !_.isNumber(value) || _.isNaN(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const getColumnWidth = (
|
||||
rows,
|
||||
accessor,
|
||||
{ maxWidth, minWidth, magicSpacing = 14 },
|
||||
) => {
|
||||
const cellLength = Math.max(
|
||||
...rows.map((row) => (`${_.get(row, accessor)}` || '').length),
|
||||
);
|
||||
let result = cellLength * magicSpacing;
|
||||
|
||||
result = minWidth ? Math.max(minWidth, result) : result;
|
||||
result = maxWidth ? Math.min(maxWidth, result) : result;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user