WIP Version 0.0.1

This commit is contained in:
Ahmed Bouhuolia
2020-05-08 04:36:04 +02:00
parent bd7eb0eb76
commit 71cc561bb2
151 changed files with 1742 additions and 1081 deletions

View File

@@ -0,0 +1,89 @@
import React, {useEffect, useMemo, useCallback, useState} from 'react';
import DashboardConnect from 'connectors/Dashboard.connector';
import {compose} from 'utils';
import useAsync from 'hooks/async';
import BalanceSheetConnect from 'connectors/BalanceSheet.connect';
import {useIntl} from 'react-intl';
import BalanceSheetHeader from './BalanceSheetHeader';
import BalanceSheetTable from './BalanceSheetTable';
import moment from 'moment';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
import BalanceSheetActionsBar from './BalanceSheetActionsBar';
import SettingsConnect from 'connectors/Settings.connect';
function BalanceSheet({
fetchBalanceSheet,
changePageTitle,
balanceSheetLoading,
getBalanceSheetIndex,
getBalanceSheet,
organizationSettings
}) {
const intl = useIntl();
const [filter, setFilter] = useState({
from_date: moment().startOf('year').format('YYYY-MM-DD'),
to_date: moment().endOf('year').format('YYYY-MM-DD'),
basis: 'cash',
display_columns_type: 'total',
display_columns_by: '',
none_zero: false,
});
const fetchHook = useAsync(async (query = filter) => {
await Promise.all([
fetchBalanceSheet({ ...query }),
]);
}, false);
// Handle fetch the data of balance sheet.
const handleFetchData = useCallback(() => { fetchHook.execute(); }, [fetchHook]);
useEffect(() => {
changePageTitle('Balance Sheet');
}, []);
// Retrieve balance sheet index by the given filter query.
const balanceSheetIndex = useMemo(() =>
getBalanceSheetIndex(filter),
[filter, getBalanceSheetIndex]);
// 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'),
};
setFilter({ ..._filter });
fetchHook.execute(_filter);
}, [setFilter, fetchHook]);
return (
<DashboardInsider>
<BalanceSheetActionsBar />
<DashboardPageContent>
<div class="financial-statement">
<BalanceSheetHeader
pageFilter={filter}
onSubmitFilter={handleFilterSubmit} />
<div class="financial-statement__body">
<BalanceSheetTable
companyName={organizationSettings.name}
loading={balanceSheetLoading}
balanceSheetIndex={balanceSheetIndex}
onFetchData={handleFetchData} />
</div>
</div>
</DashboardPageContent>
</DashboardInsider>
);
}
export default compose(
DashboardConnect,
BalanceSheetConnect,
SettingsConnect,
)(BalanceSheet);

View File

@@ -0,0 +1,63 @@
import React from 'react';
import {
NavbarGroup,
Button,
Classes,
NavbarHeading,
NavbarDivider,
Intent,
Popover,
PopoverInteractionKind,
Position,
} from '@blueprintjs/core';
import Icon from 'components/Icon';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'
import classNames from 'classnames';
import FilterDropdown from 'components/FilterDropdown';
export default function JournalActionsBar({
}) {
const filterDropdown = FilterDropdown({
fields: [],
onFilterChange: (filterConditions) => {
},
});
return (
<DashboardActionsBar>
<NavbarGroup>
<Button
className={classNames(Classes.MINIMAL, 'button--table-views')}
icon={<Icon icon='cog' />}
text='Customize Report'
/>
<NavbarDivider />
<Popover
content={filterDropdown}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text="Filter"
icon={ <Icon icon="filter" /> } />
</Popover>
<Button
className={Classes.MINIMAL}
icon={<Icon icon='file-export' />}
text='Print'
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon='file-export' />}
text='Export'
/>
</NavbarGroup>
</DashboardActionsBar>
)
}

View File

@@ -0,0 +1,113 @@
import React, {useMemo, useCallback} from 'react';
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
import {Row, Col} from 'react-grid-system';
import {
Button,
FormGroup,
MenuItem,
} from "@blueprintjs/core";
import SelectList from 'components/SelectList';
import {useIntl} from 'react-intl';
import moment from 'moment';
import Icon from 'components/Icon';
import { useFormik } from 'formik';
import * as Yup from 'yup';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy';
import RadiosAccountingBasis from '../RadiosAccountingBasis';
export default function BalanceSheetHeader({
onSubmitFilter,
pageFilter,
}) {
const intl = useIntl();
const formik = useFormik({
enableReinitialize: true,
initialValues: {
...pageFilter,
basis: 'cash',
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);
},
});
// 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);
}, []);
// Handle submit filter submit button.
const handleSubmitClick = useCallback(() => {
formik.submitForm();
}, [formik]);
const filterAccountsOptions = useMemo(() => [
{key: '', name: 'Accounts with Zero Balance'},
{key: 'all-trans', name: 'All Transactions' },
], []);
const filterAccountRenderer = useCallback((item, { handleClick, modifiers, query }) => {
return (<MenuItem text={item.name} key={item.id} onClick={handleClick} />);
}, []);
const infoIcon = useMemo(() =>
(<Icon icon="info-circle" iconSize={12} />), []);
const handleAccountingBasisChange = useCallback((value) => {
formik.setFieldValue('basis', value);
}, [formik]);
return (
<FinancialStatementHeader>
<FinancialStatementDateRange formik={formik} />
<Row>
<Col sm={3}>
<SelectDisplayColumnsBy
onItemSelect={onItemSelectDisplayColumns} />
</Col>
<Col sm={3}>
<FormGroup
label={'Filter Accounts'}
className="form-group--select-list bp3-fill"
inline={false}>
<SelectList
items={filterAccountsOptions}
itemRenderer={filterAccountRenderer}
onItemSelect={onItemSelectDisplayColumns}
popoverProps={{ minimal: true }}
filterable={false} />
</FormGroup>
</Col>
<Col sm={3}>
<RadiosAccountingBasis
selectedValue={formik.values.basis}
onChange={handleAccountingBasisChange} />
</Col>
<Col sm={3}>
<Button
type="submit"
onClick={handleSubmitClick}
disabled={formik.isSubmitting}
className={'button--submit-filter mt2'}>
{ 'Calculate Report' }
</Button>
</Col>
</Row>
</FinancialStatementHeader>
)
}

View File

@@ -0,0 +1,137 @@
import React, {useMemo, useState, useCallback, useEffect} from 'react';
import moment from 'moment';
import Money from 'components/Money';
import FinancialSheet from 'components/FinancialSheet';
import DataTable from 'components/DataTable';
import BalanceSheetConnect from 'connectors/BalanceSheet.connect';
import BalanceSheetTableConnect from 'connectors/BalanceSheetTable.connect';
import {
compose,
defaultExpanderReducer,
} from 'utils';
import SettingsConnect from 'connectors/Settings.connect';
function BalanceSheetTable({
organizationSettings,
balanceSheetAccounts,
balanceSheetColumns,
balanceSheetQuery,
onFetchData,
asDate,
loading,
}) {
const columns = useMemo(() => [
{
// Build our expander column
id: 'expander', // Make sure it has an ID
className: 'expander',
Header: ({
getToggleAllRowsExpandedProps,
isAllRowsExpanded
}) => (
<span {...getToggleAllRowsExpandedProps()} className="toggle">
{isAllRowsExpanded ?
(<span class="arrow-down" />) :
(<span class="arrow-right" />)
}
</span>
),
Cell: ({ row }) =>
// Use the row.canExpand and row.getToggleRowExpandedProps prop getter
// to build the toggle for expanding a row
row.canExpand ? (
<span
{...row.getToggleRowExpandedProps({
style: {
// We can even use the row.depth property
// and paddingLeft to indicate the depth
// of the row
paddingLeft: `${row.depth * 2}rem`,
},
className: 'toggle',
})}
>
{row.isExpanded ?
(<span class="arrow-down" />) :
(<span class="arrow-right" />)
}
</span>
) : null,
width: 20,
disableResizing: true,
},
{
Header: 'Account Name',
accessor: 'name',
className: "account_name",
},
{
Header: 'Code',
accessor: 'code',
className: "code",
},
...(balanceSheetQuery.display_columns_type === 'total') ? [
{
Header: '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 '';
},
className: "credit",
}
] : [],
...(balanceSheetQuery.display_columns_type === 'date_periods') ?
(balanceSheetColumns.map((column, index) => ({
id: `date_period_${index}`,
Header: column,
accessor: (row) => {
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,
})))
: [],
], [balanceSheetQuery, balanceSheetColumns]);
const handleFetchData = useCallback(() => {
onFetchData && onFetchData();
}, [onFetchData]);
// Calculates the default expanded rows of balance sheet table.
const expandedRows = useMemo(() =>
defaultExpanderReducer(balanceSheetAccounts, 1),
[balanceSheetAccounts]);
return (
<FinancialSheet
companyName={organizationSettings.name}
sheetType={'Balance Sheet'}
fromDate={balanceSheetQuery.from_date}
toDate={balanceSheetQuery.to_date}
basis={balanceSheetQuery.basis}
loading={loading}>
<DataTable
className="bigcapital-datatable--financial-report"
columns={columns}
data={balanceSheetAccounts}
onFetchData={handleFetchData}
expanded={expandedRows}
expandSubRows={true}
noInitialFetch={true} />
</FinancialSheet>
);
}
export default compose(
BalanceSheetConnect,
BalanceSheetTableConnect,
SettingsConnect,
)(BalanceSheetTable);

View File

@@ -0,0 +1,105 @@
import React, {useState, useCallback, useMemo} from 'react';
import {Row, Col} from 'react-grid-system';
import {momentFormatter} from 'utils';
import {DateInput} from '@blueprintjs/datetime';
import {useIntl} from 'react-intl';
import {
HTMLSelect,
FormGroup,
Intent,
Position,
} from '@blueprintjs/core';
import Icon from 'components/Icon';
import {
parseDateRangeQuery
} from 'utils';
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]);
const infoIcon = useMemo(() => (<Icon icon="info-circle" iconSize={12} />), []);
return (
<Row>
<Col sm={3}>
<FormGroup
label={intl.formatMessage({'id': 'report_date_range'})}
labelInfo={infoIcon}
minimal={true}
fill={true}>
<HTMLSelect
fill={true}
options={dateRangeOptions}
value={reportDateRange}
onChange={handleDateRangeChange} />
</FormGroup>
</Col>
<Col sm={3}>
<FormGroup
label={intl.formatMessage({'id': 'from_date'})}
labelInfo={infoIcon}
minimal={true}
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 }}
fill={true} />
</FormGroup>
</Col>
<Col sm={3}>
<FormGroup
label={intl.formatMessage({'id': 'to_date'})}
labelInfo={infoIcon}
minimal={true}
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}
intent={formik.errors.to_date && Intent.DANGER} />
</FormGroup>
</Col>
</Row>
);
}

View File

@@ -0,0 +1,9 @@
import React from 'react';
export default function FinancialStatementHeader({ children }) {
return (
<div class="financial-statement__header">
{ children }
</div>
);
}

View File

@@ -0,0 +1,103 @@
import React, { useEffect, useCallback, useState, useMemo } from 'react';
import moment from 'moment';
import GeneralLedgerTable from 'containers/FinancialStatements/GeneralLedger/GeneralLedgerTable';
import useAsync from 'hooks/async';
import DashboardConnect from 'connectors/Dashboard.connector';
import GeneralLedgerConnect from 'connectors/GeneralLedgerSheet.connect';
import GeneralLedgerHeader from './GeneralLedgerHeader';
import {compose} from 'utils';
import DashboardInsider from 'components/Dashboard/DashboardInsider'
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
import GeneralLedgerActionsBar from './GeneralLedgerActionsBar';
import AccountsConnect from 'connectors/Accounts.connector';
import SettingsConnect from 'connectors/Settings.connect';
function GeneralLedger({
changePageTitle,
getGeneralLedgerSheetIndex,
getGeneralLedgerSheet,
fetchGeneralLedger,
generalLedgerSheetLoading,
requestFetchAccounts,
organizationSettings,
}) {
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: true,
});
// Change page title of the dashboard.
useEffect(() => {
changePageTitle('General Ledger');
}, []);
const fetchHook = useAsync(() => {
return Promise.all([
requestFetchAccounts(),
]);
});
const fetchSheet = useAsync((query = filter) => {
return Promise.all([
fetchGeneralLedger(query),
]);
}, false);
const generalLedgerSheetIndex = useMemo(() =>
getGeneralLedgerSheetIndex(filter),
[getGeneralLedgerSheetIndex, filter]);
const generalLedgerSheet = useMemo(() =>
getGeneralLedgerSheet(generalLedgerSheetIndex),
[generalLedgerSheetIndex, getGeneralLedgerSheet])
// Handle fetch data of trial balance table.
const handleFetchData = useCallback(() => { fetchSheet.execute() }, [fetchSheet]);
// 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'),
};
setFilter(parsedFilter);
fetchSheet.execute(parsedFilter);
}, [setFilter, fetchSheet]);
const handleFilterChanged = () => {};
return (
<DashboardInsider>
<GeneralLedgerActionsBar onFilterChanged={handleFilterChanged} />
<DashboardPageContent>
<div class="financial-statement financial-statement--general-ledger">
<GeneralLedgerHeader
pageFilter={filter}
onSubmitFilter={handleFilterSubmit} />
<div class="financial-statement__table">
<GeneralLedgerTable
companyName={organizationSettings.name}
loading={generalLedgerSheetLoading}
data={[
... (generalLedgerSheet) ?
generalLedgerSheet.tableRows : [],
]}
onFetchData={handleFetchData} />
</div>
</div>
</DashboardPageContent>
</DashboardInsider>
);
}
export default compose(
DashboardConnect,
AccountsConnect,
GeneralLedgerConnect,
SettingsConnect,
)(GeneralLedger);

View File

@@ -0,0 +1,63 @@
import React from 'react';
import {
NavbarGroup,
Button,
Classes,
NavbarHeading,
NavbarDivider,
Intent,
Popover,
PopoverInteractionKind,
Position,
} from '@blueprintjs/core';
import Icon from 'components/Icon';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'
import classNames from 'classnames';
import FilterDropdown from 'components/FilterDropdown';
export default function GeneralLedgerActionsBar({
}) {
const filterDropdown = FilterDropdown({
fields: [],
onFilterChange: (filterConditions) => {
},
});
return (
<DashboardActionsBar>
<NavbarGroup>
<Button
className={classNames(Classes.MINIMAL, 'button--table-views')}
icon={<Icon icon='cog' />}
text='Customize Report'
/>
<NavbarDivider />
<Popover
content={filterDropdown}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text="Filter"
icon={ <Icon icon="filter" /> } />
</Popover>
<Button
className={Classes.MINIMAL}
icon={<Icon icon='file-export' />}
text='Print'
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon='file-export' />}
text='Export'
/>
</NavbarGroup>
</DashboardActionsBar>
);
}

View File

@@ -0,0 +1,97 @@
import React, {useState, useMemo, useEffect, useCallback} from 'react';
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
import {useIntl} from 'react-intl';
import {
Button,
FormGroup,
Classes,
} from '@blueprintjs/core';
import {Row, Col} from 'react-grid-system';
import {
compose,
} from 'utils';
import moment from 'moment';
import AccountsConnect from 'connectors/Accounts.connector'
import classNames from 'classnames';
import AccountsMultiSelect from 'components/AccountsMultiSelect';
import {useFormik} from 'formik';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import * as Yup from 'yup';
import RadiosAccountingBasis from '../RadiosAccountingBasis';
function GeneralLedgerHeader({
onSubmitFilter,
pageFilter,
accounts,
}) {
const intl = 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(),
to_date: Yup.date().min(Yup.ref('from_date')).required(),
}),
onSubmit(values, actions) {
onSubmitFilter(values);
actions.setSubmitting(false);
},
});
// handle submit filter submit button.
const handleSubmitClick = useCallback(() => {
formik.submitForm();
}, []);
const onAccountSelected = useCallback((selectedAccounts) => {
console.log(selectedAccounts);
}, []);
const handleAccountingBasisChange = useCallback((value) => {
formik.setFieldValue('basis', value);
}, [formik]);
return (
<FinancialStatementHeader>
<FinancialStatementDateRange formik={formik} />
<Row>
<Col sm={3}>
<FormGroup
label={'Specific Accounts'}
className={classNames('form-group--select-list', Classes.FILL)}
>
<AccountsMultiSelect
accounts={accounts}
onAccountSelected={onAccountSelected} />
</FormGroup>
</Col>
<Col sm={3}>
<RadiosAccountingBasis
onChange={handleAccountingBasisChange}
selectedValue={formik.values.basis} />
</Col>
<Col sm={3}>
<Button
type="submit"
onClick={handleSubmitClick}
disabled={formik.isSubmitting}
className={'button--submit-filter mt2'}>
{ 'Calculate Report' }
</Button>
</Col>
</Row>
</FinancialStatementHeader>
)
}
export default compose(
AccountsConnect
)(GeneralLedgerHeader);

View File

@@ -0,0 +1,165 @@
import React, {useEffect, useState, useCallback, useMemo} from 'react';
import FinancialSheet from 'components/FinancialSheet';
import DataTable from 'components/DataTable';
import Money from 'components/Money';
import moment from 'moment';
import {
defaultExpanderReducer,
} from 'utils';
const ROW_TYPE = {
CLOSING_BALANCE: 'closing_balance',
OPENING_BALANCE: 'opening_balance',
ACCOUNT: 'account_name',
TRANSACTION: 'transaction',
}
export default function GeneralLedgerTable({
companyName,
onFetchData,
loading,
data,
}) {
// 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]);
// Date accessor.
const dateAccessor = useCallback((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-MM-YYYY') : '';
}, [moment, ROW_TYPE]);
// Amount cell
const amountCell = useCallback(({ cell }) => {
const transaction = cell.row.original
if (transaction.rowType === ROW_TYPE.ACCOUNT) {
return (!cell.row.isExpanded) ?
(<Money amount={transaction.closing.amount} currency={"USD"} />) : '';
}
return (<Money amount={transaction.amount} currency={"USD"} />);
}, []);
const referenceLink = useCallback((row) => {
return (<a href="">{ row.referenceId }</a>);
});
const columns = useMemo(() => [
{
// Build our expander column
id: 'expander', // Make sure it has an ID
className: 'expander',
Header: ({
getToggleAllRowsExpandedProps,
isAllRowsExpanded
}) => (
<span {...getToggleAllRowsExpandedProps()} className="toggle">
{isAllRowsExpanded ?
(<span class="arrow-down" />) :
(<span class="arrow-right" />)
}
</span>
),
Cell: ({ row }) =>
// Use the row.canExpand and row.getToggleRowExpandedProps prop getter
// to build the toggle for expanding a row
row.canExpand ? (
<span
{...row.getToggleRowExpandedProps({
style: {
// We can even use the row.depth property
// and paddingLeft to indicate the depth
// of the row
paddingLeft: `${row.depth * 2}rem`,
},
className: 'toggle',
})}
>
{row.isExpanded ?
(<span class="arrow-down" />) :
(<span class="arrow-right" />)
}
</span>
) : null,
width: 20,
disableResizing: true,
},
{
Header: 'Account Name',
accessor: accountNameAccessor,
className: "name",
},
{
Header: 'Date',
accessor: dateAccessor,
className: "date",
},
{
Header: 'Transaction Type',
accessor: 'referenceType',
className: 'transaction_type',
},
{
Header: 'Trans. NUM',
accessor: referenceLink,
className: 'transaction_number'
},
{
Header: 'Description',
accessor: 'note',
className: 'description',
},
{
Header: 'Amount',
Cell: amountCell,
className: 'amount'
},
{
Header: 'Balance',
Cell: amountCell,
className: 'balance',
},
], []);
const handleFetchData = useCallback(() => {
onFetchData && onFetchData();
}, [onFetchData]);
// Default expanded rows of general ledger table.
const expandedRows = useMemo(() => defaultExpanderReducer(data, 1), [data]);
return (
<FinancialSheet
companyName={companyName}
sheetType={'General Ledger Sheet'}
date={new Date()}
name="general-ledger"
loading={loading}>
<DataTable
className="bigcapital-datatable--financial-report"
columns={columns}
data={data}
onFetchData={handleFetchData}
expanded={expandedRows}
virtualizedRows={true}
fixedItemSize={37}
fixedSizeHeight={1000} />
</FinancialSheet>
);
}

View File

@@ -0,0 +1,105 @@
import React, {useState, useCallback, useEffect, useMemo} from 'react';
import {compose} from 'utils';
import JournalConnect from 'connectors/Journal.connect';
import JournalHeader from 'containers/FinancialStatements/Journal/JournalHeader';
import useAsync from 'hooks/async';
import {useIntl} from 'react-intl';
import moment from 'moment';
import JournalTable from './JournalTable';
import DashboardConnect from 'connectors/Dashboard.connector';
import JournalActionsBar from './JournalActionsBar';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
import SettingsConnect from 'connectors/Settings.connect';
function Journal({
fetchJournalSheet,
getJournalSheet,
getJournalSheetIndex,
changePageTitle,
journalSheetLoading,
organizationSettings,
}) {
const [filter, setFilter] = useState({
from_date: moment().startOf('year').format('YYYY-MM-DD'),
to_date: moment().endOf('year').format('YYYY-MM-DD'),
basis: 'accural'
});
useEffect(() => {
changePageTitle('Journal Sheet');
}, []);
const fetchHook = useAsync((query = filter) => {
return Promise.all([
fetchJournalSheet(query),
]);
}, false);
// Retrieve journal sheet index by the given filter query.
const journalSheetIndex = useMemo(() =>
getJournalSheetIndex(filter),
[getJournalSheetIndex, filter]);
// Retrieve journal sheet by the given sheet index.
const journalSheet = useMemo(() =>
getJournalSheet(journalSheetIndex),
[getJournalSheet, journalSheetIndex]);
// 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'),
};
setFilter(_filter);
fetchHook.execute(_filter);
}, [fetchHook]);
const handlePrintClick = useCallback(() => {
}, []);
const handleExportClick = useCallback(() => {
}, []);
const handleFetchData = useCallback(({ sortBy, pageIndex, pageSize }) => {
fetchHook.execute();
}, [fetchHook]);
return (
<DashboardInsider>
<JournalActionsBar
onFilterChanged={() => {}}
onPrintClick={handlePrintClick}
onExportClick={handleExportClick} />
<DashboardPageContent>
<div class="financial-statement financial-statement--journal">
<JournalHeader
pageFilter={filter}
onSubmitFilter={handleFilterSubmit} />
<div class="financial-statement__table">
<JournalTable
companyName={organizationSettings.name}
data={[
...(journalSheet && journalSheet.tableRows)
? journalSheet.tableRows : []
]}
loading={journalSheetLoading}
onFetchData={handleFetchData} />
</div>
</div>
</DashboardPageContent>
</DashboardInsider>
)
}
export default compose(
JournalConnect,
DashboardConnect,
SettingsConnect,
)(Journal);

View File

@@ -0,0 +1,63 @@
import React from 'react';
import {
NavbarGroup,
Button,
Classes,
NavbarHeading,
NavbarDivider,
Intent,
Popover,
PopoverInteractionKind,
Position,
} from '@blueprintjs/core';
import Icon from 'components/Icon';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'
import classNames from 'classnames';
import FilterDropdown from 'components/FilterDropdown';
export default function JournalActionsBar({
}) {
const filterDropdown = FilterDropdown({
fields: [],
onFilterChange: (filterConditions) => {
},
});
return (
<DashboardActionsBar>
<NavbarGroup>
<Button
className={classNames(Classes.MINIMAL, 'button--table-views')}
icon={<Icon icon='cog' />}
text='Customize Report'
/>
<NavbarDivider />
<Popover
content={filterDropdown}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text="Filter"
icon={ <Icon icon="filter" /> } />
</Popover>
<Button
className={Classes.MINIMAL}
icon={<Icon icon='file-export' />}
text='Print'
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon='file-export' />}
text='Export'
/>
</NavbarGroup>
</DashboardActionsBar>
)
}

View File

@@ -0,0 +1,58 @@
import React, {useCallback} from 'react';
import {Row, Col} from 'react-grid-system';
import {
Button,
Intent,
} from '@blueprintjs/core';
import moment from 'moment';
import {useFormik} from 'formik';
import {useIntl} from 'react-intl';
import * as Yup from 'yup';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
export default function JournalHeader({
pageFilter,
onSubmitFilter,
}) {
const intl = 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(),
to_date: Yup.date().min(Yup.ref('from_date')).required(),
}),
onSubmit: (values, actions) => {
onSubmitFilter(values);
actions.setSubmitting(false);
},
});
const handleSubmitClick = useCallback(() => {
formik.submitForm();
}, [formik]);
return (
<FinancialStatementHeader>
<FinancialStatementDateRange formik={formik} />
<Row>
<Col sm={3}>
<Button
type="submit"
onClick={handleSubmitClick}
className={'button--submit-filter'}>
{ 'Run Report' }
</Button>
</Col>
</Row>
</FinancialStatementHeader>
);
}

View File

@@ -0,0 +1,100 @@
import React, {useState, useEffect, useCallback, useMemo} from 'react';
import FinancialSheet from 'components/FinancialSheet';
import DataTable from 'components/DataTable';
import {compose, defaultExpanderReducer} from 'utils';
import moment from 'moment';
import JournalConnect from 'connectors/Journal.connect';
import {
getFinancialSheet,
} from 'store/financialStatement/financialStatements.selectors';
import {connect} from 'react-redux';
import Money from 'components/Money';
function JournalSheetTable({
onFetchData,
data,
loading,
companyName,
}) {
const rowTypeFilter = (rowType, value, types) => {
return (types.indexOf(rowType) === -1) ? '' : value;
};
const exceptRowTypes = (rowType, value, types) => {
return (types.indexOf(rowType) !== -1) ? '' : value;
};
const columns = useMemo(() => [
{
Header: 'Date',
accessor: r => rowTypeFilter(r.rowType, moment(r.date).format('YYYY/MM/DD'), ['first_entry']),
className: 'date',
width: 85,
},
{
Header: 'Transaction Type',
accessor: r => rowTypeFilter(r.rowType, r.transaction_type, ['first_entry']),
className: "transaction_type",
width: 145,
},
{
Header: 'Num.',
accessor: r => rowTypeFilter(r.rowType, r.reference_id, ['first_entry']),
className: 'reference_id',
width: 70,
},
{
Header: 'Description',
accessor: 'note',
},
{
Header: 'Acc. Code',
accessor: 'account.code',
width: 120,
className: 'account_code',
},
{
Header: 'Account',
accessor: 'account.name',
},
{
Header: 'Credit',
accessor: r => exceptRowTypes(
r.rowType, (<Money amount={r.credit} currency={'USD'} />), ['space_entry']),
},
{
Header: 'Debit',
accessor: r => exceptRowTypes(
r.rowType, (<Money amount={r.debit} currency={'USD'} />), ['space_entry']),
},
], []);
const handleFetchData = useCallback((...args) => {
onFetchData && onFetchData(...args)
}, [onFetchData]);
// Default expanded rows of general journal table.
const expandedRows = useMemo(() => defaultExpanderReducer(data, 1), [data]);
return (
<FinancialSheet
companyName={companyName}
sheetType={'Journal Sheet'}
date={new Date()}
name="journal"
loading={loading}>
<DataTable
className="bigcapital-datatable--financial-report"
columns={columns}
data={data}
onFetchData={handleFetchData}
noResults={"This report does not contain any data."}
expanded={expandedRows}
noInitialFetch={true} />
</FinancialSheet>
);
}
export default compose(
JournalConnect,
)(JournalSheetTable);

View File

@@ -0,0 +1,51 @@
import React from 'react';
import {
NavbarGroup,
Button,
Classes,
NavbarHeading,
NavbarDivider,
Intent,
Popover,
PopoverInteractionKind,
Position,
} from '@blueprintjs/core';
import Icon from 'components/Icon';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'
import classNames from 'classnames';
import FilterDropdown from 'components/FilterDropdown';
export default function ProfitLossActionsBar({
}) {
const filterDropdown = FilterDropdown({
fields: [],
onFilterChange: (filterConditions) => {
},
});
return (
<DashboardActionsBar>
<NavbarGroup>
<Button
className={classNames(Classes.MINIMAL, 'button--table-views')}
icon={<Icon icon='cog' />}
text='Customize Report'
/>
<NavbarDivider />
<Button
className={Classes.MINIMAL}
icon={<Icon icon='file-export' />}
text='Print'
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon='file-export' />}
text='Export'
/>
</NavbarGroup>
</DashboardActionsBar>
);
}

View File

@@ -0,0 +1,86 @@
import React, {useState, useMemo, useCallback, useEffect} from 'react';
import moment from 'moment';
import useAsync from 'hooks/async';
import {compose} from 'utils';
import ProfitLossSheetHeader from './ProfitLossSheetHeader';
import ProfitLossSheetTable from './ProfitLossSheetTable';
import DashboardConnect from 'connectors/Dashboard.connector';
import ProfitLossSheetConnect from 'connectors/ProfitLossSheet.connect';
import DashboardInsider from 'components/Dashboard/DashboardInsider'
import DashboardPageContent from 'components/Dashboard/DashboardPageContent'
import ProfitLossActionsBar from './ProfitLossActionsBar';
import SettingsConnect from 'connectors/Settings.connect';
function ProfitLossSheet({
changePageTitle,
fetchProfitLossSheet,
getProfitLossSheetIndex,
profitLossSheetLoading,
organizationSettings,
}) {
const [filter, setFilter] = useState({
basis: 'cash',
from_date: moment().startOf('year').format('YYYY-MM-DD'),
to_date: moment().endOf('year').format('YYYY-MM-DD'),
});
// Change page title of the dashboard.
useEffect(() => {
changePageTitle('Profit/Loss Sheet');
}, [changePageTitle]);
// Fetches profit/loss sheet.
const fetchHook = useAsync((query = filter) => {
return Promise.all([
fetchProfitLossSheet(query),
]);
}, false);
// Retrieve profit/loss sheet index based on the given filter query.
const profitLossSheetIndex = useMemo(() =>
getProfitLossSheetIndex(filter),
[getProfitLossSheetIndex, filter]);
// 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'),
};
setFilter(_filter);
fetchHook.execute(_filter);
}, [fetchHook]);
// Handle fetch data of profit/loss sheet table.
const handleFetchData = useCallback(() => { fetchHook.execute(); }, [fetchHook]);
return (
<DashboardInsider>
<ProfitLossActionsBar />
<DashboardPageContent>
<div class="financial-statement">
<ProfitLossSheetHeader
pageFilter={filter}
onSubmitFilter={handleSubmitFilter} />
<div class="financial-statement__body">
<ProfitLossSheetTable
companyName={organizationSettings.name}
profitLossSheetIndex={profitLossSheetIndex}
onFetchData={handleFetchData}
loading={profitLossSheetLoading} />
</div>
</div>
</DashboardPageContent>
</DashboardInsider>
);
}
export default compose(
DashboardConnect,
ProfitLossSheetConnect,
SettingsConnect,
)(ProfitLossSheet);

View File

@@ -0,0 +1,78 @@
import React, {useCallback} from 'react';
import {Row, Col} from 'react-grid-system';
import {
Button,
} from '@blueprintjs/core';
import moment from 'moment';
import {useFormik} from 'formik';
import {useIntl} from 'react-intl';
import * as Yup from 'yup';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
import SelectsListColumnsBy from '../SelectDisplayColumnsBy';
import RadiosAccountingBasis from '../RadiosAccountingBasis';
export default function JournalHeader({
pageFilter,
onSubmitFilter,
}) {
const intl = 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(),
to_date: Yup.date().min(Yup.ref('from_date')).required(),
}),
onSubmit: (values, actions) => {
onSubmitFilter(values);
actions.setSubmitting(false);
},
});
// 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]);
const handleSubmitClick = useCallback(() => {
formik.submitForm();
}, [formik]);
const handleAccountingBasisChange = useCallback((value) => {
formik.setFieldValue('basis', value);
}, [formik]);
return (
<FinancialStatementHeader>
<FinancialStatementDateRange formik={formik} />
<Row>
<Col sm={3}>
<SelectsListColumnsBy onItemSelect={handleItemSelectDisplayColumns} />
</Col>
<Col sm={3}>
<RadiosAccountingBasis
selectedValue={formik.values.basis}
onChange={handleAccountingBasisChange} />
</Col>
<Col sm={3}>
<Button
type="submit"
onClick={handleSubmitClick}
className={'button--submit-filter mt2'}>
{ 'Run Report' }
</Button>
</Col>
</Row>
</FinancialStatementHeader>
);
}

View File

@@ -0,0 +1,138 @@
import React, {useState, useMemo, useCallback} from 'react';
import FinancialSheet from 'components/FinancialSheet';
import DataTable from 'components/DataTable';
import Money from 'components/Money';
import ProfitLossSheetConnect from 'connectors/ProfitLossSheet.connect';
import ProfitLossSheetTableConnect from 'connectors/ProfitLossTable.connect';
import { compose, defaultExpanderReducer } from 'utils';
function ProfitLossSheetTable({
loading,
onFetchData,
profitLossTableRows,
profitLossQuery,
profitLossColumns,
companyName,
}) {
const columns = useMemo(() => [
{
// Build our expander column
id: 'expander', // Make sure it has an ID
className: 'expander',
Header: ({
getToggleAllRowsExpandedProps,
isAllRowsExpanded
}) => (
<span {...getToggleAllRowsExpandedProps()} className="toggle">
{isAllRowsExpanded ?
(<span class="arrow-down" />) :
(<span class="arrow-right" />)
}
</span>
),
Cell: ({ row }) =>
// Use the row.canExpand and row.getToggleRowExpandedProps prop getter
// to build the toggle for expanding a row
row.canExpand ? (
<span
{...row.getToggleRowExpandedProps({
style: {
// We can even use the row.depth property
// and paddingLeft to indicate the depth
// of the row
paddingLeft: `${row.depth * 2}rem`,
},
className: 'toggle',
})}
>
{row.isExpanded ?
(<span class="arrow-down" />) :
(<span class="arrow-right" />)
}
</span>
) : null,
width: 20,
disableResizing: true,
},
{
Header: 'Account Name',
accessor: 'name',
className: "name",
},
{
Header: 'Acc. Code',
accessor: 'code',
className: "account_code",
},
...(profitLossQuery.display_columns_type === 'total') ? [
{
Header: 'Total',
Cell: ({ cell }) => {
const row = cell.row.original;
if (row.total) {
return (<Money amount={row.total.formatted_amount} currency={'USD'} />);
}
return '';
},
className: "total",
}
] : [],
...(profitLossQuery.display_columns_type === 'date_periods') ?
(profitLossColumns.map((column, index) => ({
id: `date_period_${index}`,
Header: column,
accessor: (row) => {
if (row.periods && row.periods[index]) {
const amount = row.periods[index].formatted_amount;
return (<Money amount={amount} currency={'USD'} />);
}
return '';
},
width: 100,
})))
: [],
], [profitLossQuery.display_columns_type, profitLossColumns]);
// Handle data table fetch data.
const handleFetchData = useCallback((...args) => {
onFetchData && onFetchData(...args);
}, [onFetchData]);
// Retrieve default expanded rows of balance sheet.
const expandedRows = useMemo(() =>
defaultExpanderReducer(profitLossTableRows, 1),
[profitLossTableRows]);
// Retrieve conditional datatable row classnames.
const rowClassNames = useCallback((row) => {
return {
[`row--${row.rowType}`]: row.rowType,
};
}, []);
return (
<FinancialSheet
companyName={companyName}
sheetType={'Profit/Loss Sheet'}
date={new Date()}
name="profit-loss-sheet"
loading={loading}
basis={profitLossQuery.basis}>
<DataTable
className="bigcapital-datatable--financial-report"
columns={columns}
data={profitLossTableRows}
onFetchData={handleFetchData}
expanded={expandedRows}
rowClassNames={rowClassNames}
noInitialFetch={true} />
</FinancialSheet>
);
}
export default compose(
ProfitLossSheetConnect,
ProfitLossSheetTableConnect,
)(ProfitLossSheetTable);

View File

@@ -0,0 +1,28 @@
import React from 'react';
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 intl = useIntl();
return (
<RadioGroup
inline={true}
label={intl.formatMessage({'id': 'accounting_basis'})}
name="basis"
onChange={handleStringChange((value) => {
onChange && onChange(value);
})}
className={'radio-group---accounting-basis'}
{...rest}>
<Radio label="Cash" value="cash" />
<Radio label="Accural" value="accural" />
</RadioGroup>
);
}

View File

@@ -0,0 +1,54 @@
import React, { useMemo, useState, useCallback } from 'react';
import SelectList from 'components/SelectList';
import {
FormGroup,
MenuItem,
} from '@blueprintjs/core';
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 : 'Select display columns by...',
[itemSelected]);
return (
<FormGroup
label={'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 }}
buttonLabel={buttonLabel}
onItemSelect={handleItemSelect}
{...selectListProps} />
</FormGroup>
);
}

View File

@@ -0,0 +1,51 @@
import React from 'react';
import {
NavbarGroup,
Button,
Classes,
NavbarHeading,
NavbarDivider,
Intent,
Popover,
PopoverInteractionKind,
Position,
} from '@blueprintjs/core';
import Icon from 'components/Icon';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'
import classNames from 'classnames';
import FilterDropdown from 'components/FilterDropdown';
export default function GeneralLedgerActionsBar({
}) {
const filterDropdown = FilterDropdown({
fields: [],
onFilterChange: (filterConditions) => {
},
});
return (
<DashboardActionsBar>
<NavbarGroup>
<Button
className={classNames(Classes.MINIMAL, 'button--table-views')}
icon={<Icon icon='cog' />}
text='Customize Report'
/>
<NavbarDivider />
<Button
className={Classes.MINIMAL}
icon={<Icon icon='file-export' />}
text='Print'
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon='file-export' />}
text='Export'
/>
</NavbarGroup>
</DashboardActionsBar>
);
}

View File

@@ -0,0 +1,91 @@
import React, { useEffect, useCallback, useState, useMemo } from 'react';
import TrialBalanceSheetHeader from "./TrialBalanceSheetHeader";
import TrialBalanceSheetTable from './TrialBalanceSheetTable';
import useAsync from 'hooks/async';
import moment from 'moment';
import {compose} from 'utils';
import TrialBalanceSheetConnect from 'connectors/TrialBalanceSheet.connect';
import DashboardConnect from 'connectors/Dashboard.connector';
import TrialBalanceActionsBar from './TrialBalanceActionsBar';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
import SettingsConnect from 'connectors/Settings.connect';
function TrialBalanceSheet({
changePageTitle,
fetchTrialBalanceSheet,
getTrialBalanceSheetIndex,
getTrialBalanceAccounts,
trialBalanceSheetLoading,
organizationSettings,
}) {
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 fetchHook = useAsync((query = filter) => {
return Promise.all([
fetchTrialBalanceSheet(query),
]);
}, false);
// handle fetch data of trial balance table.
const handleFetchData = useCallback(() => { fetchHook.execute() }, [fetchHook]);
// Retrieve balance sheet index by the given filter query.
const trialBalanceSheetIndex = useMemo(() =>
getTrialBalanceSheetIndex(filter),
[getTrialBalanceSheetIndex, filter]);
// Retrieve balance sheet accounts bu the given sheet index.
const trialBalanceAccounts = useMemo(() =>
getTrialBalanceAccounts(trialBalanceSheetIndex),
[getTrialBalanceAccounts, trialBalanceSheetIndex]);
// Change page title of the dashboard.
useEffect(() => {
changePageTitle('Trial Balance Sheet');
}, []);
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'),
};
setFilter(parsedFilter);
fetchHook.execute(parsedFilter);
}, [setFilter, fetchHook]);
return (
<DashboardInsider>
<TrialBalanceActionsBar />
<DashboardPageContent>
<div class="financial-statement">
<TrialBalanceSheetHeader
pageFilter={filter}
onSubmitFilter={handleFilterSubmit} />
<div class="financial-statement__body">
<TrialBalanceSheetTable
companyName={organizationSettings.name}
trialBalanceSheetAccounts={trialBalanceAccounts}
trialBalanceSheetIndex={trialBalanceSheetIndex}
onFetchData={handleFetchData}
loading={trialBalanceSheetLoading} />
</div>
</div>
</DashboardPageContent>
</DashboardInsider>
)
}
export default compose(
DashboardConnect,
TrialBalanceSheetConnect,
SettingsConnect,
)(TrialBalanceSheet);

View File

@@ -0,0 +1,66 @@
import React, {useState, useCallback, useMemo} from 'react';
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
import {Row, Col} from 'react-grid-system';
import {
Button,
FormGroup,
Position,
MenuItem,
RadioGroup,
Radio,
HTMLSelect,
Intent,
Popover,
} from "@blueprintjs/core";
import moment from 'moment';
import {useIntl} from 'react-intl';
import { useFormik } from 'formik';
import * as Yup from 'yup';
import Icon from 'components/Icon';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
export default function TrialBalanceSheetHeader({
pageFilter,
onSubmitFilter,
}) {
const intl = 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(),
to_date: Yup.date().min(Yup.ref('from_date')).required(),
}),
onSubmit: (values, actions) => {
onSubmitFilter(values);
actions.setSubmitting(false);
}
});
const handleSubmitClick = useCallback(() => {
formik.submitForm();
}, [formik]);
return (
<FinancialStatementHeader>
<FinancialStatementDateRange formik={formik} />
<Row>
<Col sm={3}>
<Button
type="submit"
onClick={handleSubmitClick}
disabled={formik.isSubmitting}
className={'button--submit-filter'}>
{ 'Run Report' }
</Button>
</Col>
</Row>
</FinancialStatementHeader>
);
}

View File

@@ -0,0 +1,105 @@
import React, {useEffect, useState, useCallback, useMemo} from 'react';
import FinancialSheet from 'components/FinancialSheet';
import DataTable from 'components/DataTable';
import Money from 'components/Money';
export default function TrialBalanceSheetTable({
trialBalanceSheetAccounts,
trialBalanceSheetIndex,
onFetchData,
loading,
companyName,
}) {
const [data, setData] = useState([]);
const columns = useMemo(() => [
{
// Build our expander column
id: 'expander', // Make sure it has an ID
className: 'expander',
Header: ({
getToggleAllRowsExpandedProps,
isAllRowsExpanded
}) => (
<span {...getToggleAllRowsExpandedProps()} className="toggle">
{isAllRowsExpanded ?
(<span class="arrow-down" />) :
(<span class="arrow-right" />)
}
</span>
),
Cell: ({ row }) =>
// Use the row.canExpand and row.getToggleRowExpandedProps prop getter
// to build the toggle for expanding a row
row.canExpand ? (
<span
{...row.getToggleRowExpandedProps({
style: {
// We can even use the row.depth property
// and paddingLeft to indicate the depth
// of the row
paddingLeft: `${row.depth * 2}rem`,
},
className: 'toggle',
})}
>
{row.isExpanded ?
(<span class="arrow-down" />) :
(<span class="arrow-right" />)
}
</span>
) : null,
width: 20,
disableResizing: true,
},
{
Header: 'Account Name',
accessor: 'name',
className: "name",
},
{
Header: 'Code',
accessor: 'code',
className: "code",
width: 120,
},
{
Header: 'Credit',
accessor: r => (<Money amount={r.credit} currency="USD" />),
className: 'credit',
width: 120,
},
{
Header: 'Debit',
accessor: r => (<Money amount={r.debit} currency="USD" />),
className: 'debit',
width: 120,
},
{
Header: 'Balance',
accessor: r => (<Money amount={r.balance} currency="USD" />),
className: 'balance',
width: 120,
}
], []);
const handleFetchData = useCallback(() => {
onFetchData && onFetchData();
}, [onFetchData]);
return (
<FinancialSheet
companyName={companyName}
sheetType={'Trial Balance Sheet'}
date={new Date()}
name="trial-balance"
loading={loading}>
<DataTable
className="bigcapital-datatable--financial-report"
columns={columns}
data={trialBalanceSheetAccounts}
onFetchData={handleFetchData} />
</FinancialSheet>
);
}