mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 22:00:31 +00:00
WIP
This commit is contained in:
@@ -120,6 +120,10 @@ export default [
|
||||
text: 'Trial Balance Sheet',
|
||||
href: '/dashboard/accounting/trial-balance-sheet',
|
||||
},
|
||||
{
|
||||
text: 'Journal',
|
||||
href: '/dashboard/accounting/journal-sheet',
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
|
||||
19
client/src/connectors/Journal.connect.js
Normal file
19
client/src/connectors/Journal.connect.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import {connect} from 'react-redux';
|
||||
import {
|
||||
fetchJournalSheet
|
||||
} from 'store/financialStatement/financialStatements.actions';
|
||||
import {
|
||||
getFinancialSheetIndexByQuery,
|
||||
getFinancialSheet,
|
||||
} from 'store/financialStatement/financialStatements.selectors';
|
||||
|
||||
export const mapStateToProps = (state, props) => ({
|
||||
getJournalSheetIndex: (query) => getFinancialSheetIndexByQuery(state.financialStatements.journalSheets, query),
|
||||
getJournalSheet: (index) => getFinancialSheet(state.financialStatements.journalSheets, index),
|
||||
});
|
||||
|
||||
export const mapDispatchToProps = (dispatch) => ({
|
||||
fetchJournalSheet: (query) => dispatch(fetchJournalSheet({ query })),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps);
|
||||
@@ -78,16 +78,12 @@ function BalanceSheetTable({
|
||||
}
|
||||
},
|
||||
}))),
|
||||
|
||||
|
||||
|
||||
], [balanceSheetColumns]);
|
||||
], [balanceSheetColumns, balanceSheetQuery]);
|
||||
|
||||
const [data, setData] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!balanceSheet) { return; }
|
||||
|
||||
setData([
|
||||
{
|
||||
name: 'Assets',
|
||||
@@ -106,9 +102,6 @@ function BalanceSheetTable({
|
||||
])
|
||||
}, [])
|
||||
|
||||
// if (balanceSheets.length > 0) {
|
||||
// setData(balanceSheets[0].balance_sheet);
|
||||
// }
|
||||
return (
|
||||
<FinancialSheet
|
||||
companyTitle={'Facebook, Incopration'}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import React, {useState, useEffect, useMemo} from 'react';
|
||||
import {compose} from 'utils';
|
||||
import LoadingIndicator from 'components/LoadingIndicator';
|
||||
import JournalConnect from 'connectors/Journal.connect';
|
||||
import JournalHeader from 'containers/Dashboard/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';
|
||||
|
||||
function Journal({
|
||||
fetchJournalSheet,
|
||||
getJournalSheet,
|
||||
getJournalSheetIndex,
|
||||
changePageTitle,
|
||||
}) {
|
||||
const [filter, setFilter] = useState({
|
||||
from_date: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
to_date: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
});
|
||||
const [reload, setReload] = useState(false);
|
||||
|
||||
const fetchHook = useAsync(async () => {
|
||||
await Promise.all([
|
||||
fetchJournalSheet(filter),
|
||||
]);
|
||||
setReload(false);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
changePageTitle('Journal');
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (reload) {
|
||||
fetchHook.execute();
|
||||
}
|
||||
}, [reload, fetchHook]);
|
||||
|
||||
const journalSheetIndex = useMemo(() => {
|
||||
return getJournalSheetIndex(filter);
|
||||
}, [filter, getJournalSheetIndex]);
|
||||
|
||||
const handleFilterSubmit = (filter) => {
|
||||
setFilter({
|
||||
...filter,
|
||||
from_date: moment(filter.from_date).format('YYYY-MM-DD'),
|
||||
to_date: moment(filter.to_date).format('YYYY-MM-DD'),
|
||||
});
|
||||
setReload(true);
|
||||
};
|
||||
return (
|
||||
<div class="financial-statement">
|
||||
<JournalHeader
|
||||
pageFilter={filter}
|
||||
onSubmitFilter={handleFilterSubmit} />
|
||||
|
||||
<div class="financial-statement__body">
|
||||
<LoadingIndicator loading={fetchHook.pending}>
|
||||
<JournalTable
|
||||
journalIndex={journalSheetIndex} />
|
||||
</LoadingIndicator>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default compose(
|
||||
JournalConnect,
|
||||
DashboardConnect,
|
||||
)(Journal);
|
||||
@@ -0,0 +1,120 @@
|
||||
import React, {useState, useMemo, useEffect} from 'react';
|
||||
import FinancialStatementHeader from 'containers/Dashboard/FinancialStatements/FinancialStatementHeader';
|
||||
import {Row, Col} from 'react-grid-system';
|
||||
import {
|
||||
Button,
|
||||
FormGroup,
|
||||
Position,
|
||||
HTMLSelect,
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
import {DateInput} from '@blueprintjs/datetime';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
momentFormatter,
|
||||
parseDateRangeQuery,
|
||||
} from 'utils';
|
||||
import {useIntl} from 'react-intl';
|
||||
|
||||
export default function JournalHeader({
|
||||
pageFilter,
|
||||
onSubmitFilter,
|
||||
}) {
|
||||
const intl = useIntl();
|
||||
|
||||
const [filter, setFilter] = useState({
|
||||
...pageFilter,
|
||||
from_date: moment(pageFilter.from_date).toDate(),
|
||||
to_date: moment(pageFilter.to_date).toDate()
|
||||
});
|
||||
|
||||
const setFilterByKey = (name, value) => {
|
||||
setFilter({ ...filter, [name]: value });
|
||||
};
|
||||
const [reportDateRange, setReportDateRange] = useState('this_year');
|
||||
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'},
|
||||
];
|
||||
const handleDateChange = (name) => (date) => {
|
||||
setReportDateRange('custom');
|
||||
setFilterByKey(name, date);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (reportDateRange === 'custom') { return; }
|
||||
const dateRange = parseDateRangeQuery(reportDateRange);
|
||||
|
||||
if (dateRange) {
|
||||
setFilter((filter) => ({ ...filter, ...dateRange }));
|
||||
}
|
||||
}, [reportDateRange]);
|
||||
|
||||
const handleSubmitClick = () => { onSubmitFilter(filter); };
|
||||
|
||||
return (
|
||||
<FinancialStatementHeader>
|
||||
<Row>
|
||||
<Col sm={3}>
|
||||
<FormGroup
|
||||
label={intl.formatMessage({'id': 'report_date_range'})}
|
||||
minimal={true}
|
||||
fill={true}>
|
||||
|
||||
<HTMLSelect
|
||||
fill={true}
|
||||
options={dateRangeOptions}
|
||||
value={reportDateRange}
|
||||
onChange={(event) => setReportDateRange(event.target.value)} />
|
||||
</FormGroup>
|
||||
</Col>
|
||||
|
||||
<Col sm={3}>
|
||||
<FormGroup
|
||||
label={intl.formatMessage({'id': 'from_date'})}
|
||||
minimal={true}
|
||||
fill={true}>
|
||||
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={filter.from_date}
|
||||
onChange={handleDateChange('from_date')}
|
||||
popoverProps={{ position: Position.BOTTOM }}
|
||||
fill={true} />
|
||||
</FormGroup>
|
||||
</Col>
|
||||
|
||||
<Col sm={3}>
|
||||
<FormGroup
|
||||
label={intl.formatMessage({'id': 'to_date'})}
|
||||
minimal={true}
|
||||
fill={true}>
|
||||
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={filter.to_date}
|
||||
onChange={handleDateChange('to_date')}
|
||||
popoverProps={{ position: Position.BOTTOM }}
|
||||
fill={true} />
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Col sm={3}>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
onClick={handleSubmitClick}>
|
||||
{ 'Run Report' }
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
</FinancialStatementHeader>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import React, {useState, useEffect, useMemo} from 'react';
|
||||
import FinancialSheet from 'components/FinancialSheet';
|
||||
import DataTable from 'components/DataTable';
|
||||
import {compose} from 'utils';
|
||||
import moment from 'moment';
|
||||
import JournalConnect from 'connectors/Journal.connect';
|
||||
import {
|
||||
getFinancialSheet,
|
||||
} from 'store/financialStatement/financialStatements.selectors';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
function JournalSheetTable({
|
||||
journalIndex,
|
||||
journalTableData,
|
||||
}) {
|
||||
const columns = useMemo(() => [
|
||||
{
|
||||
Header: 'Date',
|
||||
accessor: r => moment(r.date).format('YYYY/MM/DD'),
|
||||
className: 'date',
|
||||
},
|
||||
{
|
||||
Header: 'Account Name',
|
||||
accessor: 'account.name',
|
||||
},
|
||||
{
|
||||
Header: 'Transaction Type',
|
||||
accessor: 'transaction_type',
|
||||
className: "transaction_type",
|
||||
},
|
||||
{
|
||||
Header: 'Num.',
|
||||
accessor: 'reference_id',
|
||||
className: 'reference_id',
|
||||
},
|
||||
{
|
||||
Header: 'Note',
|
||||
accessor: 'note',
|
||||
},
|
||||
{
|
||||
Header: 'Credit',
|
||||
accessor: 'credit',
|
||||
},
|
||||
{
|
||||
Header: 'Debit',
|
||||
accessor: 'debit',
|
||||
},
|
||||
], []);
|
||||
|
||||
return (
|
||||
<FinancialSheet
|
||||
companyTitle={'Facebook, Incopration'}
|
||||
sheetType={'Balance Sheet'}
|
||||
date={[]}>
|
||||
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={journalTableData} />
|
||||
|
||||
</FinancialSheet>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const journalTableData = [];
|
||||
const journalSheet = getFinancialSheet(state.financialStatements.journalSheets, props.journalIndex);
|
||||
|
||||
if (journalSheet && journalSheet.journal) {
|
||||
journalSheet.journal.forEach((journal) => {
|
||||
journal.entries.forEach((entry, index) => {
|
||||
journalTableData.push({ ...entry, index });
|
||||
});
|
||||
journalTableData.push({
|
||||
credit: journal.credit,
|
||||
debit: journal.debit,
|
||||
total: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
return {
|
||||
journalSheet,
|
||||
journalTableData,
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps),
|
||||
JournalConnect,
|
||||
)(JournalSheetTable);
|
||||
@@ -95,5 +95,12 @@ export default [
|
||||
component: LazyLoader({
|
||||
loader: () => import('containers/Dashboard/FinancialStatements/ProfitLossSheet/ProfitLossSheet')
|
||||
}),
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `${BASE_URL}/accounting/journal-sheet`,
|
||||
name: 'dashboard.accounting.journal.sheet',
|
||||
component: LazyLoader({
|
||||
loader: () => import('containers/Dashboard/FinancialStatements/Journal/Journal')
|
||||
}),
|
||||
},
|
||||
];
|
||||
@@ -3,7 +3,7 @@ import t from 'store/types';
|
||||
|
||||
export const fetchGeneralLedger = ({ query }) => {
|
||||
return (dispatch) => new Promise((resolve, reject) => {
|
||||
ApiService.get('/financial_statements/general_ledger').then((response) => {
|
||||
ApiService.get('/financial_statements/general_ledger', { params: query }).then((response) => {
|
||||
dispatch({
|
||||
type: t.GENERAL_LEDGER_STATEMENT_SET,
|
||||
data: response.data,
|
||||
@@ -28,7 +28,7 @@ export const fetchBalanceSheet = ({ query }) => {
|
||||
|
||||
export const fetchTrialBalanceSheet = ({ query }) => {
|
||||
return (dispatch) => new Promise((resolve, reject) => {
|
||||
ApiService.get('/financial_statements/trial_balance_sheet').then((response) => {
|
||||
ApiService.get('/financial_statements/trial_balance_sheet', { params: query }).then((response) => {
|
||||
dispatch({
|
||||
type: t.TRAIL_BALANCE_STATEMENT_SET,
|
||||
data: response.data,
|
||||
@@ -40,7 +40,7 @@ export const fetchTrialBalanceSheet = ({ query }) => {
|
||||
|
||||
export const fetchProfitLossSheet = ({ query }) => {
|
||||
return (dispatch) => new Promise((resolve, reject) => {
|
||||
ApiService.get('/financial_statements/profit_loss_sheet').then((response) => {
|
||||
ApiService.get('/financial_statements/profit_loss_sheet', { params: query }).then((response) => {
|
||||
dispatch({
|
||||
type: t.PROFIT_LOSS_STATEMENT_SET,
|
||||
data: response.data,
|
||||
@@ -48,4 +48,17 @@ export const fetchProfitLossSheet = ({ query }) => {
|
||||
resolve(response.data);
|
||||
}).catch((error) => { reject(error); });
|
||||
})
|
||||
};
|
||||
|
||||
export const fetchJournalSheet = ({ query }) => {
|
||||
return (dispatch) => new Promise((resolve, reject) => {
|
||||
ApiService.get('/financial_statements/journal', { params: query }).then((response) => {
|
||||
dispatch({
|
||||
type: t.JOURNAL_SHEET_SET,
|
||||
data: response.data,
|
||||
query: response.data.query,
|
||||
});
|
||||
resolve(response.data);
|
||||
}).catch(error => { reject(error); });
|
||||
});
|
||||
};
|
||||
@@ -1,12 +1,16 @@
|
||||
import { createReducer } from '@reduxjs/toolkit';
|
||||
import t from 'store/types';
|
||||
import {getBalanceSheetIndexByQuery, getTrialBalanceSheetIndex} from './financialStatements.selectors';
|
||||
import { actionComplete } from '@syncfusion/ej2-react-grids';
|
||||
import {
|
||||
getBalanceSheetIndexByQuery,
|
||||
getTrialBalanceSheetIndex,
|
||||
getFinancialSheetIndexByQuery,
|
||||
} from './financialStatements.selectors';
|
||||
|
||||
const initialState = {
|
||||
balanceSheets: [],
|
||||
trialBalanceSheets: [],
|
||||
generalLedger: [],
|
||||
journalSheets: [],
|
||||
};
|
||||
|
||||
export default createReducer(initialState, {
|
||||
@@ -37,5 +41,20 @@ export default createReducer(initialState, {
|
||||
} else {
|
||||
state.trailBalanceSheet.push(trailBalanceSheet);
|
||||
}
|
||||
},
|
||||
|
||||
[t.JOURNAL_SHEET_SET]: (state, action) => {
|
||||
const index = getFinancialSheetIndexByQuery(state.journalSheets, action.query);
|
||||
console.log(index, 'INDEX');
|
||||
|
||||
const journal = {
|
||||
query: action.data.query,
|
||||
journal: action.data.journal,
|
||||
};
|
||||
if (index !== -1) {
|
||||
state.journalSheets[index] = journal;
|
||||
} else {
|
||||
state.journalSheets.push(journal);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,6 +1,49 @@
|
||||
import {getObjectDiff} from 'utils';
|
||||
|
||||
|
||||
// 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
|
||||
));
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve financial statement sheet by the given sheet index.
|
||||
* @param {array} sheets
|
||||
* @param {number} index
|
||||
*/
|
||||
export const getFinancialSheet = (sheets, index) => {
|
||||
return (typeof sheets[index] !== 'undefined') ? sheets[index] : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 : [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve financial statement query by the given sheet index.
|
||||
* @param {array} sheets
|
||||
* @param {number} index
|
||||
*/
|
||||
export const getFinancialSheetsQuery = (sheets, index) => {
|
||||
const sheet = getFinancialSheet(sheets, index);
|
||||
return (sheet && sheet.query) ? sheet.columns : {};
|
||||
};
|
||||
|
||||
|
||||
// Balance Sheet.
|
||||
export const getBalanceSheetByQuery = (balanceSheets, query) => {
|
||||
return balanceSheets.find(balanceSheet => {
|
||||
|
||||
@@ -4,4 +4,5 @@ export default {
|
||||
GENERAL_LEDGER_STATEMENT_SET: 'GENERAL_LEDGER_STATEMENT_SET',
|
||||
BALANCE_SHEET_STATEMENT_SET: 'BALANCE_SHEET_STATEMENT_SET',
|
||||
TRAIL_BALANCE_STATEMENT_SET: 'TRAIL_BALANCE_STATEMENT_SET',
|
||||
JOURNAL_SHEET_SET: 'JOURNAL_SHEET_SET',
|
||||
}
|
||||
Reference in New Issue
Block a user