mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
feat: cashflow accounts.
This commit is contained in:
@@ -12,10 +12,12 @@ export const TABLES = {
|
|||||||
ACCOUNTS: 'accounts',
|
ACCOUNTS: 'accounts',
|
||||||
MANUAL_JOURNALS: 'manual_journal',
|
MANUAL_JOURNALS: 'manual_journal',
|
||||||
EXPENSES: 'expenses',
|
EXPENSES: 'expenses',
|
||||||
|
CASHFLOW_ACCOUNTS: 'cashflow_accounts',
|
||||||
|
CASHFLOW_Transactions: 'cashflow_transactions',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TABLE_SIZE = {
|
export const TABLE_SIZE = {
|
||||||
COMPACT: 'compact',
|
COMPACT: 'compact',
|
||||||
SMALL: 'small',
|
SMALL: 'small',
|
||||||
MEDIUM: 'medium',
|
MEDIUM: 'medium',
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
NavbarGroup,
|
||||||
|
Classes,
|
||||||
|
NavbarDivider,
|
||||||
|
Alignment,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Icon,
|
||||||
|
DashboardRowsHeightButton,
|
||||||
|
FormattedMessage as T,
|
||||||
|
} from 'components';
|
||||||
|
|
||||||
|
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||||
|
import { useCashFlowAccountsContext } from './CashFlowAccountsProvider';
|
||||||
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
|
import withSettings from '../../Settings/withSettings';
|
||||||
|
import withSettingsActions from '../../Settings/withSettingsActions';
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cash Flow accounts actions bar.
|
||||||
|
*/
|
||||||
|
function CashFlowAccountsActionsBar({
|
||||||
|
// #withDialogActions
|
||||||
|
openDialog,
|
||||||
|
|
||||||
|
// #withSettings
|
||||||
|
cashflowTableSize,
|
||||||
|
|
||||||
|
// #withSettingsActions
|
||||||
|
addSetting,
|
||||||
|
}) {
|
||||||
|
// Handle table row size change.
|
||||||
|
const handleTableRowSizeChange = (size) => {
|
||||||
|
addSetting('cashflowAccounts', 'tableSize', size);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle click a refresh
|
||||||
|
const handleRefreshBtnClick = () => {};
|
||||||
|
|
||||||
|
// Handle add bank account.
|
||||||
|
const handleAddBankAccount = () => {
|
||||||
|
openDialog('account-form', {});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle add cash account.
|
||||||
|
const handleAddCashAccount = () => {
|
||||||
|
openDialog('account-form', {});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardActionsBar>
|
||||||
|
<NavbarGroup>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
// className={classNames(Classes.MINIMAL, 'button--table-views')}
|
||||||
|
icon={<Icon icon={'plus-24'} iconSize={20} />}
|
||||||
|
text={<T id={'cash_flow.label.add_cash_account'} />}
|
||||||
|
onClick={handleAddBankAccount}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon={'plus-24'} iconSize={20} />}
|
||||||
|
text={<T id={'cash_flow.label.add_bank_account'} />}
|
||||||
|
onClick={handleAddCashAccount}
|
||||||
|
/>
|
||||||
|
<NavbarDivider />
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
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'} />}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon="file-import-16" iconSize={16} />}
|
||||||
|
text={<T id={'import'} />}
|
||||||
|
/>
|
||||||
|
<NavbarDivider />
|
||||||
|
<DashboardRowsHeightButton
|
||||||
|
initialValue={cashflowTableSize}
|
||||||
|
onChange={handleTableRowSizeChange}
|
||||||
|
/>
|
||||||
|
<NavbarDivider />
|
||||||
|
</NavbarGroup>
|
||||||
|
<NavbarGroup align={Alignment.RIGHT}>
|
||||||
|
<Button
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon="refresh-16" iconSize={14} />}
|
||||||
|
onClick={handleRefreshBtnClick}
|
||||||
|
/>
|
||||||
|
</NavbarGroup>
|
||||||
|
</DashboardActionsBar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default compose(
|
||||||
|
withDialogActions,
|
||||||
|
withSettingsActions,
|
||||||
|
withSettings(({ cashflowSettings }) => ({
|
||||||
|
cashflowTableSize: cashflowSettings?.tableSize,
|
||||||
|
})),
|
||||||
|
)(CashFlowAccountsActionsBar);
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { DataTable, TableFastCell } from 'components';
|
||||||
|
import { TABLES } from 'common/tables';
|
||||||
|
|
||||||
|
import TableVirtualizedListRows from 'components/Datatable/TableVirtualizedRows';
|
||||||
|
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
|
||||||
|
import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
|
||||||
|
import withSettings from '../../Settings/withSettings';
|
||||||
|
|
||||||
|
import { useMemorizedColumnsWidths } from '../../../hooks';
|
||||||
|
import { useCashFlowAccountsContext } from './CashFlowAccountsProvider';
|
||||||
|
import { useCashFlowAccountsTableColumns } from './components';
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cash flow accounts data table.
|
||||||
|
*/
|
||||||
|
function CashFlowAccountsDataTable({
|
||||||
|
// #withSettings
|
||||||
|
cashflowTableSize,
|
||||||
|
}) {
|
||||||
|
// Retrieve list context.
|
||||||
|
const {
|
||||||
|
cashflowAccounts,
|
||||||
|
isCashFlowAccountsFetching,
|
||||||
|
isCashFlowAccountsLoading,
|
||||||
|
} = useCashFlowAccountsContext();
|
||||||
|
|
||||||
|
// Retrieve table columns.
|
||||||
|
const columns = useCashFlowAccountsTableColumns();
|
||||||
|
|
||||||
|
// Local storage memorizing columns widths.
|
||||||
|
const [initialColumnsWidths, , handleColumnResizing] =
|
||||||
|
useMemorizedColumnsWidths(TABLES.CASHFLOW_ACCOUNTS);
|
||||||
|
return (
|
||||||
|
<DataTable
|
||||||
|
noInitialFetch={true}
|
||||||
|
columns={columns}
|
||||||
|
data={cashflowAccounts}
|
||||||
|
selectionColumn={true}
|
||||||
|
sticky={true}
|
||||||
|
loading={isCashFlowAccountsLoading}
|
||||||
|
headerLoading={isCashFlowAccountsLoading}
|
||||||
|
progressBarLoading={isCashFlowAccountsFetching}
|
||||||
|
expandColumnSpace={1}
|
||||||
|
expandToggleColumn={2}
|
||||||
|
selectionColumnWidth={45}
|
||||||
|
TableCellRenderer={TableFastCell}
|
||||||
|
TableRowsRenderer={TableVirtualizedListRows}
|
||||||
|
TableLoadingRenderer={TableSkeletonRows}
|
||||||
|
// #TableVirtualizedListRows props.
|
||||||
|
vListrowHeight={cashflowTableSize == 'small' ? 40 : 42}
|
||||||
|
vListOverscanRowCount={0}
|
||||||
|
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||||
|
initialColumnsWidths={initialColumnsWidths}
|
||||||
|
onColumnResizing={handleColumnResizing}
|
||||||
|
size={cashflowTableSize}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withSettings(({ cashflowSettings }) => ({
|
||||||
|
cashflowTableSize: cashflowSettings?.tableSize,
|
||||||
|
})),
|
||||||
|
)(CashFlowAccountsDataTable);
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import 'style/pages/CashFlow/CashFlowAccounts/List.scss';
|
||||||
|
|
||||||
|
import { DashboardPageContent, DashboardContentTable } from 'components';
|
||||||
|
|
||||||
|
import { CashFlowAccountsProvider } from './CashFlowAccountsProvider';
|
||||||
|
|
||||||
|
import CashFlowAccountsActionsBar from './CashFlowAccountsActionsBar';
|
||||||
|
import CashFlowAccountsDataTable from './CashFlowAccountsDataTable';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cash flow accounts list.
|
||||||
|
*/
|
||||||
|
function CashFlowAccountsList({}) {
|
||||||
|
return (
|
||||||
|
<CashFlowAccountsProvider>
|
||||||
|
<CashFlowAccountsActionsBar />
|
||||||
|
<DashboardPageContent>
|
||||||
|
<DashboardContentTable>
|
||||||
|
<CashFlowAccountsDataTable />
|
||||||
|
</DashboardContentTable>
|
||||||
|
</DashboardPageContent>
|
||||||
|
</CashFlowAccountsProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CashFlowAccountsList;
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
|
|
||||||
|
import { useCashflowAccounts } from 'hooks/query';
|
||||||
|
|
||||||
|
const CashFlowAccountsContext = React.createContext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cash Flow data provider.
|
||||||
|
*/
|
||||||
|
function CashFlowAccountsProvider({ query, tableStateChanged, ...props }) {
|
||||||
|
// Fetch cash flow list .
|
||||||
|
const {
|
||||||
|
data: cashflowAccounts,
|
||||||
|
isFetching: isCashFlowAccountsFetching,
|
||||||
|
isLoading: isCashFlowAccountsLoading,
|
||||||
|
} = useCashflowAccounts(query, { keepPreviousData: true });
|
||||||
|
|
||||||
|
// Provider payload.
|
||||||
|
const provider = {
|
||||||
|
cashflowAccounts,
|
||||||
|
isCashFlowAccountsFetching,
|
||||||
|
isCashFlowAccountsLoading,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardInsider name={'cashflow-accounts'}>
|
||||||
|
<CashFlowAccountsContext.Provider value={provider} {...props} />
|
||||||
|
</DashboardInsider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useCashFlowAccountsContext = () =>
|
||||||
|
React.useContext(CashFlowAccountsContext);
|
||||||
|
export { CashFlowAccountsProvider, useCashFlowAccountsContext };
|
||||||
89
src/containers/CashFlow/CashFlowAccounts/components.js
Normal file
89
src/containers/CashFlow/CashFlowAccounts/components.js
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import intl from 'react-intl-universal';
|
||||||
|
import { Intent, Tag } from '@blueprintjs/core';
|
||||||
|
import { isBlank } from 'utils';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account code accessor.
|
||||||
|
*/
|
||||||
|
export const AccountCodeAccessor = (row) =>
|
||||||
|
!isBlank(row.code) ? (
|
||||||
|
<Tag minimal={true} round={true} intent={Intent.NONE}>
|
||||||
|
{row.code}
|
||||||
|
</Tag>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Balance cell.
|
||||||
|
*/
|
||||||
|
export const BalanceCell = ({ cell }) => {
|
||||||
|
const account = cell.row.original;
|
||||||
|
|
||||||
|
return account.amount !== null ? (
|
||||||
|
<span>{account.formatted_amount}</span>
|
||||||
|
) : (
|
||||||
|
<span class="placeholder">—</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account cell.
|
||||||
|
*/
|
||||||
|
const AccountCell = ({ row }) => {
|
||||||
|
const account = row.original;
|
||||||
|
return <Link to={`/account/${account.id}/transactions`}>{account.name}</Link>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve Cash flow table columns.
|
||||||
|
*/
|
||||||
|
export function useCashFlowAccountsTableColumns() {
|
||||||
|
return React.useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
id: 'name',
|
||||||
|
Header: intl.get('account_name'),
|
||||||
|
accessor: 'name',
|
||||||
|
Cell: AccountCell,
|
||||||
|
|
||||||
|
className: 'account_name',
|
||||||
|
width: 200,
|
||||||
|
textOverview: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'code',
|
||||||
|
Header: intl.get('code'),
|
||||||
|
accessor: 'code',
|
||||||
|
className: 'code',
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'type',
|
||||||
|
Header: intl.get('type'),
|
||||||
|
accessor: 'account_type_label',
|
||||||
|
className: 'type',
|
||||||
|
width: 140,
|
||||||
|
textOverview: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'currency',
|
||||||
|
Header: intl.get('currency'),
|
||||||
|
accessor: 'currency_code',
|
||||||
|
width: 75,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'balance',
|
||||||
|
Header: intl.get('balance'),
|
||||||
|
accessor: 'amount',
|
||||||
|
className: 'balance',
|
||||||
|
Cell: BalanceCell,
|
||||||
|
width: 150,
|
||||||
|
align: 'right',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -16,6 +16,9 @@ export default (mapState) => {
|
|||||||
accountsSettings: state.settings.data.accounts,
|
accountsSettings: state.settings.data.accounts,
|
||||||
customersSettings: state.settings.data.customers,
|
customersSettings: state.settings.data.customers,
|
||||||
vendorsSettings: state.settings.data.vendors,
|
vendorsSettings: state.settings.data.vendors,
|
||||||
|
cashflowSettings: state.settings.data.cashflowAccounts,
|
||||||
|
cashflowTransactionsSettings: state.settings.data.cashflowTransactions,
|
||||||
|
|
||||||
};
|
};
|
||||||
return mapState ? mapState(mapped, state, props) : mapped;
|
return mapState ? mapState(mapped, state, props) : mapped;
|
||||||
};
|
};
|
||||||
|
|||||||
75
src/hooks/query/cashflowAccounts.js
Normal file
75
src/hooks/query/cashflowAccounts.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { useMutation, useQueryClient } from 'react-query';
|
||||||
|
import { useRequestQuery } from '../useQueryRequest';
|
||||||
|
import useApiRequest from '../useRequest';
|
||||||
|
import t from './types';
|
||||||
|
|
||||||
|
const commonInvalidateQueries = (queryClient) => {
|
||||||
|
// queryClient.invalidateQueries(t.CASH_FLOW_TRANSACTIONS);
|
||||||
|
queryClient.invalidateQueries(t.CASH_FLOW_TRANSACTION);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve accounts list.
|
||||||
|
*/
|
||||||
|
export function useCashflowAccounts(query, props) {
|
||||||
|
return useRequestQuery(
|
||||||
|
[t.CASH_FLOW_ACCOUNTS, query],
|
||||||
|
{ method: 'get', url: 'cashflow/accounts', params: query },
|
||||||
|
{
|
||||||
|
select: (res) => res.data.cashflow_accounts,
|
||||||
|
defaultData: [],
|
||||||
|
...props,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Retrieve account transactions list.
|
||||||
|
*/
|
||||||
|
export function useCashflowTransactions(id, props) {
|
||||||
|
return useRequestQuery(
|
||||||
|
[t.CASH_FLOW_TRANSACTIONS, id],
|
||||||
|
{ method: 'get', url: `cashflow/account/${1000}/transactions` },
|
||||||
|
{
|
||||||
|
select: (res) => res.data.cashflow_transactions,
|
||||||
|
defaultData: [],
|
||||||
|
...props,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Money in owner contribution .
|
||||||
|
*/
|
||||||
|
export function useCreateCashflowTransaction(props) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(values) => apiRequest.post('cashflow/transactions', values),
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
// Invalidate queries.
|
||||||
|
commonInvalidateQueries(queryClient);
|
||||||
|
},
|
||||||
|
...props,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given sale invoice.
|
||||||
|
*/
|
||||||
|
export function useDeleteCashflowTransaction(props) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useMutation((id) => apiRequest.delete(`cashflow/transactions/${id}`), {
|
||||||
|
onSuccess: (res, id) => {
|
||||||
|
queryClient.invalidateQueries([t.CASH_FLOW_TRANSACTION, id]);
|
||||||
|
|
||||||
|
// Invalidate queries.
|
||||||
|
commonInvalidateQueries(queryClient);
|
||||||
|
},
|
||||||
|
...props,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -44,8 +44,14 @@ const initialState = {
|
|||||||
tableSize: 'medium',
|
tableSize: 'medium',
|
||||||
},
|
},
|
||||||
accounts: {
|
accounts: {
|
||||||
tableSize: 'medium'
|
tableSize: 'medium',
|
||||||
}
|
},
|
||||||
|
cashflowAccounts: {
|
||||||
|
tableSize: 'medium',
|
||||||
|
},
|
||||||
|
cashflowTransactions: {
|
||||||
|
tableSize: 'medium',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
33
src/style/pages/CashFlow/CashFlowAccounts/List.scss
Normal file
33
src/style/pages/CashFlow/CashFlowAccounts/List.scss
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
.dashboard__insider--cashflow-accounts {
|
||||||
|
.bigcapital-datatable {
|
||||||
|
.table {
|
||||||
|
.tbody {
|
||||||
|
.td.balance {
|
||||||
|
.cell-inner {
|
||||||
|
> span {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.account_name {
|
||||||
|
.bp3-popover-wrapper--inactive-semafro {
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 6px;
|
||||||
|
float: right;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
.bp3-popover-wrapper--account-desc {
|
||||||
|
border-bottom-color: #bbb;
|
||||||
|
}
|
||||||
|
.inactive-semafro {
|
||||||
|
height: 7px;
|
||||||
|
width: 7px;
|
||||||
|
background: #bbb;
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user