mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 15:20:34 +00:00
feat: exclude bank transactions in bulk
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { param } from 'express-validator';
|
import { body, param, query } from 'express-validator';
|
||||||
import { NextFunction, Request, Response, Router, query } from 'express';
|
import { NextFunction, Request, Response, Router } from 'express';
|
||||||
import BaseController from '../BaseController';
|
import BaseController from '../BaseController';
|
||||||
import { ExcludeBankTransactionsApplication } from '@/services/Banking/Exclude/ExcludeBankTransactionsApplication';
|
import { ExcludeBankTransactionsApplication } from '@/services/Banking/Exclude/ExcludeBankTransactionsApplication';
|
||||||
|
import { map, parseInt, trim } from 'lodash';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class ExcludeBankTransactionsController extends BaseController {
|
export class ExcludeBankTransactionsController extends BaseController {
|
||||||
@@ -15,9 +16,21 @@ export class ExcludeBankTransactionsController extends BaseController {
|
|||||||
public router() {
|
public router() {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
|
router.put(
|
||||||
|
'/transactions/exclude',
|
||||||
|
[body('ids').exists()],
|
||||||
|
this.validationResult,
|
||||||
|
this.excludeBulkBankTransactions.bind(this)
|
||||||
|
);
|
||||||
|
router.put(
|
||||||
|
'/transactions/unexclude',
|
||||||
|
[body('ids').exists()],
|
||||||
|
this.validationResult,
|
||||||
|
this.unexcludeBulkBankTransactins.bind(this)
|
||||||
|
);
|
||||||
router.put(
|
router.put(
|
||||||
'/transactions/:transactionId/exclude',
|
'/transactions/:transactionId/exclude',
|
||||||
[param('transactionId').exists()],
|
[param('transactionId').exists().toInt()],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
this.excludeBankTransaction.bind(this)
|
this.excludeBankTransaction.bind(this)
|
||||||
);
|
);
|
||||||
@@ -94,6 +107,63 @@ export class ExcludeBankTransactionsController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exclude bank transactions in bulk.
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {Response} res
|
||||||
|
* @param {NextFunction} next
|
||||||
|
*/
|
||||||
|
private async excludeBulkBankTransactions(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const { ids } = this.matchedBodyData(req);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.excludeBankTransactionApp.excludeBankTransactions(
|
||||||
|
tenantId,
|
||||||
|
ids
|
||||||
|
);
|
||||||
|
return res.status(200).send({
|
||||||
|
message: 'The given bank transactions have been excluded',
|
||||||
|
ids,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unexclude the given bank transactions in bulk.
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {Response} res
|
||||||
|
* @param {NextFunction} next
|
||||||
|
* @returns {Promise<Response | null>}
|
||||||
|
*/
|
||||||
|
private async unexcludeBulkBankTransactins(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<Response | null> {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const { ids } = this.matchedBodyData(req);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.excludeBankTransactionApp.unexcludeBankTransactions(
|
||||||
|
tenantId,
|
||||||
|
ids
|
||||||
|
);
|
||||||
|
return res.status(200).send({
|
||||||
|
message: 'The given bank transactions have been excluded',
|
||||||
|
ids,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the excluded uncategorized bank transactions.
|
* Retrieves the excluded uncategorized bank transactions.
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
@@ -109,7 +179,6 @@ export class ExcludeBankTransactionsController extends BaseController {
|
|||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
const filter = this.matchedBodyData(req);
|
const filter = this.matchedBodyData(req);
|
||||||
|
|
||||||
console.log('123');
|
|
||||||
try {
|
try {
|
||||||
const data =
|
const data =
|
||||||
await this.excludeBankTransactionApp.getExcludedBankTransactions(
|
await this.excludeBankTransactionApp.getExcludedBankTransactions(
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { ExcludeBankTransaction } from './ExcludeBankTransaction';
|
||||||
|
import PromisePool from '@supercharge/promise-pool';
|
||||||
|
import { castArray } from 'lodash';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class ExcludeBankTransactions {
|
||||||
|
@Inject()
|
||||||
|
private excludeBankTransaction: ExcludeBankTransaction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exclude bank transactions in bulk.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} bankTransactionIds
|
||||||
|
*/
|
||||||
|
public async excludeBankTransactions(
|
||||||
|
tenantId: number,
|
||||||
|
bankTransactionIds: Array<number> | number
|
||||||
|
) {
|
||||||
|
const _bankTransactionIds = castArray(bankTransactionIds);
|
||||||
|
|
||||||
|
await PromisePool.withConcurrency(1)
|
||||||
|
.for(_bankTransactionIds)
|
||||||
|
.process(async (bankTransactionId: number) => {
|
||||||
|
return this.excludeBankTransaction.excludeBankTransaction(
|
||||||
|
tenantId,
|
||||||
|
bankTransactionId
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ import { ExcludeBankTransaction } from './ExcludeBankTransaction';
|
|||||||
import { UnexcludeBankTransaction } from './UnexcludeBankTransaction';
|
import { UnexcludeBankTransaction } from './UnexcludeBankTransaction';
|
||||||
import { GetExcludedBankTransactionsService } from './GetExcludedBankTransactions';
|
import { GetExcludedBankTransactionsService } from './GetExcludedBankTransactions';
|
||||||
import { ExcludedBankTransactionsQuery } from './_types';
|
import { ExcludedBankTransactionsQuery } from './_types';
|
||||||
|
import { UnexcludeBankTransactions } from './UnexcludeBankTransactions';
|
||||||
|
import { ExcludeBankTransactions } from './ExcludeBankTransactions';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class ExcludeBankTransactionsApplication {
|
export class ExcludeBankTransactionsApplication {
|
||||||
@@ -15,6 +17,12 @@ export class ExcludeBankTransactionsApplication {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private getExcludedBankTransactionsService: GetExcludedBankTransactionsService;
|
private getExcludedBankTransactionsService: GetExcludedBankTransactionsService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private excludeBankTransactionsService: ExcludeBankTransactions;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private unexcludeBankTransactionsService: UnexcludeBankTransactions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks a bank transaction as excluded.
|
* Marks a bank transaction as excluded.
|
||||||
* @param {number} tenantId - The ID of the tenant.
|
* @param {number} tenantId - The ID of the tenant.
|
||||||
@@ -56,4 +64,36 @@ export class ExcludeBankTransactionsApplication {
|
|||||||
filter
|
filter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exclude the given bank transactions in bulk.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {Array<number> | number} bankTransactionIds
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public excludeBankTransactions(
|
||||||
|
tenantId: number,
|
||||||
|
bankTransactionIds: Array<number> | number
|
||||||
|
): Promise<void> {
|
||||||
|
return this.excludeBankTransactionsService.excludeBankTransactions(
|
||||||
|
tenantId,
|
||||||
|
bankTransactionIds
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exclude the given bank transactions in bulk.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {Array<number> | number} bankTransactionIds
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public unexcludeBankTransactions(
|
||||||
|
tenantId: number,
|
||||||
|
bankTransactionIds: Array<number> | number
|
||||||
|
): Promise<void> {
|
||||||
|
return this.unexcludeBankTransactionsService.unexcludeBankTransactions(
|
||||||
|
tenantId,
|
||||||
|
bankTransactionIds
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import PromisePool from '@supercharge/promise-pool';
|
||||||
|
import { UnexcludeBankTransaction } from './UnexcludeBankTransaction';
|
||||||
|
import { castArray } from 'lodash';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class UnexcludeBankTransactions {
|
||||||
|
@Inject()
|
||||||
|
private unexcludeBankTransaction: UnexcludeBankTransaction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unexclude bank transactions in bulk.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} bankTransactionIds
|
||||||
|
*/
|
||||||
|
public async unexcludeBankTransactions(
|
||||||
|
tenantId: number,
|
||||||
|
bankTransactionIds: Array<number> | number
|
||||||
|
) {
|
||||||
|
const _bankTransactionIds = castArray(bankTransactionIds);
|
||||||
|
|
||||||
|
await PromisePool.withConcurrency(1)
|
||||||
|
.for(_bankTransactionIds)
|
||||||
|
.process(async (bankTransactionId: number) => {
|
||||||
|
return this.unexcludeBankTransaction.unexcludeBankTransaction(
|
||||||
|
tenantId,
|
||||||
|
bankTransactionId
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
PopoverInteractionKind,
|
PopoverInteractionKind,
|
||||||
Position,
|
Position,
|
||||||
|
Intent,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
@@ -18,6 +19,7 @@ import {
|
|||||||
DashboardActionsBar,
|
DashboardActionsBar,
|
||||||
DashboardRowsHeightButton,
|
DashboardRowsHeightButton,
|
||||||
FormattedMessage as T,
|
FormattedMessage as T,
|
||||||
|
AppToaster,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
|
|
||||||
import { CashFlowMenuItems } from './utils';
|
import { CashFlowMenuItems } from './utils';
|
||||||
@@ -33,6 +35,13 @@ import withSettings from '@/containers/Settings/withSettings';
|
|||||||
import withSettingsActions from '@/containers/Settings/withSettingsActions';
|
import withSettingsActions from '@/containers/Settings/withSettingsActions';
|
||||||
|
|
||||||
import { compose } from '@/utils';
|
import { compose } from '@/utils';
|
||||||
|
import { withBanking } from '../withBanking';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import {
|
||||||
|
useExcludeUncategorizedTransactions,
|
||||||
|
useUnexcludeUncategorizedTransaction,
|
||||||
|
useUnexcludeUncategorizedTransactions,
|
||||||
|
} from '@/hooks/query/bank-rules';
|
||||||
|
|
||||||
function AccountTransactionsActionsBar({
|
function AccountTransactionsActionsBar({
|
||||||
// #withDialogActions
|
// #withDialogActions
|
||||||
@@ -43,6 +52,10 @@ function AccountTransactionsActionsBar({
|
|||||||
|
|
||||||
// #withSettingsActions
|
// #withSettingsActions
|
||||||
addSetting,
|
addSetting,
|
||||||
|
|
||||||
|
// #withBanking
|
||||||
|
uncategorizedTransationsIdsSelected,
|
||||||
|
excludedTransactionsIdsSelected,
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { accountId } = useAccountTransactionsContext();
|
const { accountId } = useAccountTransactionsContext();
|
||||||
@@ -87,6 +100,54 @@ function AccountTransactionsActionsBar({
|
|||||||
refresh();
|
refresh();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
mutateAsync: excludeUncategorizedTransactions,
|
||||||
|
isLoading: isExcludingLoading,
|
||||||
|
} = useExcludeUncategorizedTransactions();
|
||||||
|
|
||||||
|
const {
|
||||||
|
mutateAsync: unexcludeUncategorizedTransactions,
|
||||||
|
isLoading: isUnexcludingLoading,
|
||||||
|
} = useUnexcludeUncategorizedTransactions();
|
||||||
|
|
||||||
|
// Handles the exclude uncategorized transactions in bulk.
|
||||||
|
const handleExcludeUncategorizedBtnClick = () => {
|
||||||
|
excludeUncategorizedTransactions({
|
||||||
|
ids: uncategorizedTransationsIdsSelected,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: 'The selected transactions have been excluded.',
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: 'Something went wrong',
|
||||||
|
intent: Intent.DANGER,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handles the unexclude categorized button click.
|
||||||
|
const handleUnexcludeUncategorizedBtnClick = () => {
|
||||||
|
unexcludeUncategorizedTransactions({
|
||||||
|
ids: excludedTransactionsIdsSelected,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: 'The selected transactions have been unexcluded.',
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: 'Something went wrong',
|
||||||
|
intent: Intent.DANGER,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardActionsBar>
|
<DashboardActionsBar>
|
||||||
<NavbarGroup>
|
<NavbarGroup>
|
||||||
@@ -129,6 +190,28 @@ function AccountTransactionsActionsBar({
|
|||||||
onChange={handleTableRowSizeChange}
|
onChange={handleTableRowSizeChange}
|
||||||
/>
|
/>
|
||||||
<NavbarDivider />
|
<NavbarDivider />
|
||||||
|
|
||||||
|
{!isEmpty(uncategorizedTransationsIdsSelected) && (
|
||||||
|
<Button
|
||||||
|
icon={<Icon icon="disable" iconSize={16} />}
|
||||||
|
text={'Exclude'}
|
||||||
|
onClick={handleExcludeUncategorizedBtnClick}
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
disable={isExcludingLoading}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isEmpty(excludedTransactionsIdsSelected) && (
|
||||||
|
<Button
|
||||||
|
icon={<Icon icon="disable" iconSize={16} />}
|
||||||
|
text={'Unexclude'}
|
||||||
|
onClick={handleUnexcludeUncategorizedBtnClick}
|
||||||
|
className={Classes.MINIMAL}
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
disable={isUnexcludingLoading}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
|
|
||||||
<NavbarGroup align={Alignment.RIGHT}>
|
<NavbarGroup align={Alignment.RIGHT}>
|
||||||
@@ -164,4 +247,13 @@ export default compose(
|
|||||||
withSettings(({ cashflowTransactionsSettings }) => ({
|
withSettings(({ cashflowTransactionsSettings }) => ({
|
||||||
cashflowTansactionsTableSize: cashflowTransactionsSettings?.tableSize,
|
cashflowTansactionsTableSize: cashflowTransactionsSettings?.tableSize,
|
||||||
})),
|
})),
|
||||||
|
withBanking(
|
||||||
|
({
|
||||||
|
uncategorizedTransationsIdsSelected,
|
||||||
|
excludedTransactionsIdsSelected,
|
||||||
|
}) => ({
|
||||||
|
uncategorizedTransationsIdsSelected,
|
||||||
|
excludedTransactionsIdsSelected,
|
||||||
|
}),
|
||||||
|
),
|
||||||
)(AccountTransactionsActionsBar);
|
)(AccountTransactionsActionsBar);
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ function AccountTransactionsDataTable({
|
|||||||
|
|
||||||
// #withBankingActions
|
// #withBankingActions
|
||||||
setUncategorizedTransactionIdForMatching,
|
setUncategorizedTransactionIdForMatching,
|
||||||
|
setUncategorizedTransactionsSelected,
|
||||||
}) {
|
}) {
|
||||||
// Retrieve table columns.
|
// Retrieve table columns.
|
||||||
const columns = useAccountUncategorizedTransactionsColumns();
|
const columns = useAccountUncategorizedTransactionsColumns();
|
||||||
@@ -73,12 +74,19 @@ function AccountTransactionsDataTable({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle selected rows change.
|
||||||
|
const handleSelectedRowsChange = (selected) => {
|
||||||
|
const _selectedIds = selected?.map((row) => row.original.id);
|
||||||
|
setUncategorizedTransactionsSelected(_selectedIds);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CashflowTransactionsTable
|
<CashflowTransactionsTable
|
||||||
noInitialFetch={true}
|
noInitialFetch={true}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={uncategorizedTransactions || []}
|
data={uncategorizedTransactions || []}
|
||||||
sticky={true}
|
sticky={true}
|
||||||
|
selectionColumn={true}
|
||||||
loading={isUncategorizedTransactionsLoading}
|
loading={isUncategorizedTransactionsLoading}
|
||||||
headerLoading={isUncategorizedTransactionsLoading}
|
headerLoading={isUncategorizedTransactionsLoading}
|
||||||
expandColumnSpace={1}
|
expandColumnSpace={1}
|
||||||
@@ -99,6 +107,7 @@ function AccountTransactionsDataTable({
|
|||||||
'There is no uncategorized transactions in the current account.'
|
'There is no uncategorized transactions in the current account.'
|
||||||
}
|
}
|
||||||
className="table-constrant"
|
className="table-constrant"
|
||||||
|
onSelectedRowsChange={handleSelectedRowsChange}
|
||||||
payload={{
|
payload={{
|
||||||
onExclude: handleExcludeTransaction,
|
onExclude: handleExcludeTransaction,
|
||||||
onCategorize: handleCategorizeBtnClick,
|
onCategorize: handleCategorizeBtnClick,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
|
import * as R from 'ramda';
|
||||||
import {
|
import {
|
||||||
DataTable,
|
DataTable,
|
||||||
TableFastCell,
|
TableFastCell,
|
||||||
@@ -19,11 +19,20 @@ import { useExcludedTransactionsBoot } from './ExcludedTransactionsTableBoot';
|
|||||||
|
|
||||||
import { ActionsMenu } from './_components';
|
import { ActionsMenu } from './_components';
|
||||||
import { useUnexcludeUncategorizedTransaction } from '@/hooks/query/bank-rules';
|
import { useUnexcludeUncategorizedTransaction } from '@/hooks/query/bank-rules';
|
||||||
|
import {
|
||||||
|
WithBankingActionsProps,
|
||||||
|
withBankingActions,
|
||||||
|
} from '../../withBankingActions';
|
||||||
|
|
||||||
|
interface ExcludeTransactionsTableProps extends WithBankingActionsProps {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the recognized account transactions datatable.
|
* Renders the recognized account transactions datatable.
|
||||||
*/
|
*/
|
||||||
export function ExcludedTransactionsTable() {
|
function ExcludedTransactionsTableRoot({
|
||||||
|
// #withBankingActions
|
||||||
|
setExcludedTransactionsSelected,
|
||||||
|
}: ExcludeTransactionsTableProps) {
|
||||||
const { excludedBankTransactions } = useExcludedTransactionsBoot();
|
const { excludedBankTransactions } = useExcludedTransactionsBoot();
|
||||||
const { mutateAsync: unexcludeBankTransaction } =
|
const { mutateAsync: unexcludeBankTransaction } =
|
||||||
useUnexcludeUncategorizedTransaction();
|
useUnexcludeUncategorizedTransaction();
|
||||||
@@ -55,6 +64,12 @@ export function ExcludedTransactionsTable() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle selected rows change.
|
||||||
|
const handleSelectedRowsChange = (selected) => {
|
||||||
|
const _selectedIds = selected?.map((row) => row.original.id);
|
||||||
|
setExcludedTransactionsSelected(_selectedIds);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CashflowTransactionsTable
|
<CashflowTransactionsTable
|
||||||
noInitialFetch={true}
|
noInitialFetch={true}
|
||||||
@@ -80,6 +95,8 @@ export function ExcludedTransactionsTable() {
|
|||||||
onColumnResizing={handleColumnResizing}
|
onColumnResizing={handleColumnResizing}
|
||||||
noResults={'There is no excluded bank transactions.'}
|
noResults={'There is no excluded bank transactions.'}
|
||||||
className="table-constrant"
|
className="table-constrant"
|
||||||
|
selectionColumn={true}
|
||||||
|
onSelectedRowsChange={handleSelectedRowsChange}
|
||||||
payload={{
|
payload={{
|
||||||
onRestore: handleRestoreClick,
|
onRestore: handleRestoreClick,
|
||||||
}}
|
}}
|
||||||
@@ -87,6 +104,10 @@ export function ExcludedTransactionsTable() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ExcludedTransactionsTable = R.compose(withBankingActions)(
|
||||||
|
ExcludedTransactionsTableRoot,
|
||||||
|
);
|
||||||
|
|
||||||
const DashboardConstrantTable = styled(DataTable)`
|
const DashboardConstrantTable = styled(DataTable)`
|
||||||
.table {
|
.table {
|
||||||
.thead {
|
.thead {
|
||||||
|
|||||||
@@ -1,8 +1,27 @@
|
|||||||
import { ExcludedTransactionsTable } from "../ExcludedTransactions/ExcludedTransactionsTable";
|
// @ts-nocheck
|
||||||
import { ExcludedBankTransactionsTableBoot } from "../ExcludedTransactions/ExcludedTransactionsTableBoot";
|
import { useEffect } from 'react';
|
||||||
import { AccountTransactionsCard } from "./AccountTransactionsCard";
|
import * as R from 'ramda';
|
||||||
|
import {
|
||||||
|
WithBankingActionsProps,
|
||||||
|
withBankingActions,
|
||||||
|
} from '../../withBankingActions';
|
||||||
|
import { ExcludedTransactionsTable } from '../ExcludedTransactions/ExcludedTransactionsTable';
|
||||||
|
import { ExcludedBankTransactionsTableBoot } from '../ExcludedTransactions/ExcludedTransactionsTableBoot';
|
||||||
|
import { AccountTransactionsCard } from './AccountTransactionsCard';
|
||||||
|
|
||||||
|
interface AccountExcludedTransactionsProps extends WithBankingActionsProps {}
|
||||||
|
|
||||||
|
function AccountExcludedTransactionsRoot({
|
||||||
|
// #withBankingActions
|
||||||
|
resetExcludedTransactionsSelected,
|
||||||
|
}: AccountExcludedTransactionsProps) {
|
||||||
|
useEffect(
|
||||||
|
() => () => {
|
||||||
|
resetExcludedTransactionsSelected();
|
||||||
|
},
|
||||||
|
[resetExcludedTransactionsSelected],
|
||||||
|
);
|
||||||
|
|
||||||
export function AccountExcludedTransactions() {
|
|
||||||
return (
|
return (
|
||||||
<ExcludedBankTransactionsTableBoot>
|
<ExcludedBankTransactionsTableBoot>
|
||||||
<AccountTransactionsCard>
|
<AccountTransactionsCard>
|
||||||
@@ -11,3 +30,7 @@ export function AccountExcludedTransactions() {
|
|||||||
</ExcludedBankTransactionsTableBoot>
|
</ExcludedBankTransactionsTableBoot>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const AccountExcludedTransactions = R.compose(withBankingActions)(
|
||||||
|
AccountExcludedTransactionsRoot,
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,8 +1,26 @@
|
|||||||
|
import * as R from 'ramda';
|
||||||
|
import { useEffect } from 'react';
|
||||||
import AccountTransactionsUncategorizedTable from '../AccountTransactionsUncategorizedTable';
|
import AccountTransactionsUncategorizedTable from '../AccountTransactionsUncategorizedTable';
|
||||||
import { AccountUncategorizedTransactionsBoot } from '../AllTransactionsUncategorizedBoot';
|
import { AccountUncategorizedTransactionsBoot } from '../AllTransactionsUncategorizedBoot';
|
||||||
import { AccountTransactionsCard } from './AccountTransactionsCard';
|
import { AccountTransactionsCard } from './AccountTransactionsCard';
|
||||||
|
import {
|
||||||
|
WithBankingActionsProps,
|
||||||
|
withBankingActions,
|
||||||
|
} from '../../withBankingActions';
|
||||||
|
|
||||||
|
interface AccountUncategorizedTransactionsAllRootProps
|
||||||
|
extends WithBankingActionsProps {}
|
||||||
|
|
||||||
|
function AccountUncategorizedTransactionsAllRoot({
|
||||||
|
resetUncategorizedTransactionsSelected,
|
||||||
|
}: AccountUncategorizedTransactionsAllRootProps) {
|
||||||
|
useEffect(
|
||||||
|
() => () => {
|
||||||
|
resetUncategorizedTransactionsSelected();
|
||||||
|
},
|
||||||
|
[resetUncategorizedTransactionsSelected],
|
||||||
|
);
|
||||||
|
|
||||||
export function AccountUncategorizedTransactionsAll() {
|
|
||||||
return (
|
return (
|
||||||
<AccountUncategorizedTransactionsBoot>
|
<AccountUncategorizedTransactionsBoot>
|
||||||
<AccountTransactionsCard>
|
<AccountTransactionsCard>
|
||||||
@@ -11,3 +29,7 @@ export function AccountUncategorizedTransactionsAll() {
|
|||||||
</AccountUncategorizedTransactionsBoot>
|
</AccountUncategorizedTransactionsBoot>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const AccountUncategorizedTransactionsAll = R.compose(
|
||||||
|
withBankingActions,
|
||||||
|
)(AccountUncategorizedTransactionsAllRoot);
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ export const withBanking = (mapState) => {
|
|||||||
|
|
||||||
reconcileMatchingTransactionPendingAmount:
|
reconcileMatchingTransactionPendingAmount:
|
||||||
state.plaid.openReconcileMatchingTransaction.pending,
|
state.plaid.openReconcileMatchingTransaction.pending,
|
||||||
|
|
||||||
|
uncategorizedTransationsIdsSelected:
|
||||||
|
state.plaid.uncategorizedTransactionsSelected,
|
||||||
|
|
||||||
|
excludedTransactionsIdsSelected: state.plaid.excludedTransactionsSelected,
|
||||||
};
|
};
|
||||||
return mapState ? mapState(mapped, state, props) : mapped;
|
return mapState ? mapState(mapped, state, props) : mapped;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import {
|
|||||||
setUncategorizedTransactionIdForMatching,
|
setUncategorizedTransactionIdForMatching,
|
||||||
openReconcileMatchingTransaction,
|
openReconcileMatchingTransaction,
|
||||||
closeReconcileMatchingTransaction,
|
closeReconcileMatchingTransaction,
|
||||||
|
setUncategorizedTransactionsSelected,
|
||||||
|
resetUncategorizedTransactionsSelected,
|
||||||
|
resetExcludedTransactionsSelected,
|
||||||
|
setExcludedTransactionsSelected,
|
||||||
} from '@/store/banking/banking.reducer';
|
} from '@/store/banking/banking.reducer';
|
||||||
|
|
||||||
export interface WithBankingActionsProps {
|
export interface WithBankingActionsProps {
|
||||||
@@ -13,6 +17,12 @@ export interface WithBankingActionsProps {
|
|||||||
) => void;
|
) => void;
|
||||||
openReconcileMatchingTransaction: (pendingAmount: number) => void;
|
openReconcileMatchingTransaction: (pendingAmount: number) => void;
|
||||||
closeReconcileMatchingTransaction: () => void;
|
closeReconcileMatchingTransaction: () => void;
|
||||||
|
|
||||||
|
setUncategorizedTransactionsSelected: (ids: Array<string | number>) => void;
|
||||||
|
resetUncategorizedTransactionsSelected: () => void;
|
||||||
|
|
||||||
|
setExcludedTransactionsSelected: (ids: Array<string | number>) => void;
|
||||||
|
resetExcludedTransactionsSelected: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDipatchToProps = (dispatch: any): WithBankingActionsProps => ({
|
const mapDipatchToProps = (dispatch: any): WithBankingActionsProps => ({
|
||||||
@@ -28,6 +38,24 @@ const mapDipatchToProps = (dispatch: any): WithBankingActionsProps => ({
|
|||||||
dispatch(openReconcileMatchingTransaction({ pending: pendingAmount })),
|
dispatch(openReconcileMatchingTransaction({ pending: pendingAmount })),
|
||||||
closeReconcileMatchingTransaction: () =>
|
closeReconcileMatchingTransaction: () =>
|
||||||
dispatch(closeReconcileMatchingTransaction()),
|
dispatch(closeReconcileMatchingTransaction()),
|
||||||
|
|
||||||
|
setUncategorizedTransactionsSelected: (ids: Array<string | number>) =>
|
||||||
|
dispatch(
|
||||||
|
setUncategorizedTransactionsSelected({
|
||||||
|
transactionIds: ids,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
resetUncategorizedTransactionsSelected: () =>
|
||||||
|
dispatch(resetUncategorizedTransactionsSelected()),
|
||||||
|
|
||||||
|
setExcludedTransactionsSelected: (ids: Array<string | number>) =>
|
||||||
|
dispatch(
|
||||||
|
setExcludedTransactionsSelected({
|
||||||
|
ids,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
resetExcludedTransactionsSelected: () =>
|
||||||
|
dispatch(resetExcludedTransactionsSelected()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const withBankingActions = connect<
|
export const withBankingActions = connect<
|
||||||
|
|||||||
@@ -195,6 +195,20 @@ export function useGetBankTransactionsMatches(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onValidateExcludeUncategorizedTransaction = (queryClient) => {
|
||||||
|
// Invalidate queries.
|
||||||
|
queryClient.invalidateQueries(QUERY_KEY.EXCLUDED_BANK_TRANSACTIONS_INFINITY);
|
||||||
|
queryClient.invalidateQueries(
|
||||||
|
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
||||||
|
);
|
||||||
|
// Invalidate accounts.
|
||||||
|
queryClient.invalidateQueries(t.ACCOUNTS);
|
||||||
|
queryClient.invalidateQueries(t.ACCOUNT);
|
||||||
|
|
||||||
|
// invalidate bank account summary.
|
||||||
|
queryClient.invalidateQueries(QUERY_KEY.BANK_ACCOUNT_SUMMARY_META);
|
||||||
|
};
|
||||||
|
|
||||||
type ExcludeUncategorizedTransactionValue = number;
|
type ExcludeUncategorizedTransactionValue = number;
|
||||||
|
|
||||||
interface ExcludeUncategorizedTransactionRes {}
|
interface ExcludeUncategorizedTransactionRes {}
|
||||||
@@ -228,19 +242,7 @@ export function useExcludeUncategorizedTransaction(
|
|||||||
),
|
),
|
||||||
{
|
{
|
||||||
onSuccess: (res, id) => {
|
onSuccess: (res, id) => {
|
||||||
// Invalidate queries.
|
onValidateExcludeUncategorizedTransaction(queryClient);
|
||||||
queryClient.invalidateQueries(
|
|
||||||
QUERY_KEY.EXCLUDED_BANK_TRANSACTIONS_INFINITY,
|
|
||||||
);
|
|
||||||
queryClient.invalidateQueries(
|
|
||||||
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
|
||||||
);
|
|
||||||
// Invalidate accounts.
|
|
||||||
queryClient.invalidateQueries(t.ACCOUNTS);
|
|
||||||
queryClient.invalidateQueries(t.ACCOUNT);
|
|
||||||
|
|
||||||
// invalidate bank account summary.
|
|
||||||
queryClient.invalidateQueries(QUERY_KEY.BANK_ACCOUNT_SUMMARY_META);
|
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
},
|
},
|
||||||
@@ -281,19 +283,83 @@ export function useUnexcludeUncategorizedTransaction(
|
|||||||
),
|
),
|
||||||
{
|
{
|
||||||
onSuccess: (res, id) => {
|
onSuccess: (res, id) => {
|
||||||
// Invalidate queries.
|
onValidateExcludeUncategorizedTransaction(queryClient);
|
||||||
queryClient.invalidateQueries(
|
},
|
||||||
QUERY_KEY.EXCLUDED_BANK_TRANSACTIONS_INFINITY,
|
...options,
|
||||||
);
|
},
|
||||||
queryClient.invalidateQueries(
|
);
|
||||||
t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY,
|
}
|
||||||
);
|
|
||||||
// Invalidate accounts.
|
|
||||||
queryClient.invalidateQueries(t.ACCOUNTS);
|
|
||||||
queryClient.invalidateQueries(t.ACCOUNT);
|
|
||||||
|
|
||||||
// Invalidate bank account summary.
|
type ExcludeBankTransactionsValue = { ids: Array<number | string> };
|
||||||
queryClient.invalidateQueries(QUERY_KEY.BANK_ACCOUNT_SUMMARY_META);
|
interface ExcludeBankTransactionsResponse {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Excludes the uncategorized bank transactions in bulk.
|
||||||
|
* @param {UseMutationResult<ExcludeBankTransactionsResponse, Error, ExcludeBankTransactionValue>} options
|
||||||
|
* @returns {UseMutationResult<ExcludeBankTransactionsResponse, Error, ExcludeBankTransactionValue>}
|
||||||
|
*/
|
||||||
|
export function useExcludeUncategorizedTransactions(
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
ExcludeBankTransactionsResponse,
|
||||||
|
Error,
|
||||||
|
ExcludeBankTransactionsValue
|
||||||
|
>,
|
||||||
|
): UseMutationResult<
|
||||||
|
ExcludeBankTransactionsResponse,
|
||||||
|
Error,
|
||||||
|
ExcludeBankTransactionsValue
|
||||||
|
> {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useMutation<
|
||||||
|
ExcludeBankTransactionsResponse,
|
||||||
|
Error,
|
||||||
|
ExcludeBankTransactionsValue
|
||||||
|
>(
|
||||||
|
(value: { ids: Array<number | string> }) =>
|
||||||
|
apiRequest.put(`/cashflow/transactions/exclude`, { ids: value.ids }),
|
||||||
|
{
|
||||||
|
onSuccess: (res, id) => {
|
||||||
|
onValidateExcludeUncategorizedTransaction(queryClient);
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnexcludeBankTransactionsValue = { ids: Array<number | string> };
|
||||||
|
interface UnexcludeBankTransactionsResponse {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Excludes the uncategorized bank transactions in bulk.
|
||||||
|
* @param {UseMutationResult<UnexcludeBankTransactionsResponse, Error, ExcludeBankTransactionValue>} options
|
||||||
|
* @returns {UseMutationResult<UnexcludeBankTransactionsResponse, Error, ExcludeBankTransactionValue>}
|
||||||
|
*/
|
||||||
|
export function useUnexcludeUncategorizedTransactions(
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
UnexcludeBankTransactionsResponse,
|
||||||
|
Error,
|
||||||
|
UnexcludeBankTransactionsValue
|
||||||
|
>,
|
||||||
|
): UseMutationResult<
|
||||||
|
UnexcludeBankTransactionsResponse,
|
||||||
|
Error,
|
||||||
|
UnexcludeBankTransactionsValue
|
||||||
|
> {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useMutation<
|
||||||
|
UnexcludeBankTransactionsResponse,
|
||||||
|
Error,
|
||||||
|
UnexcludeBankTransactionsValue
|
||||||
|
>(
|
||||||
|
(value: { ids: Array<number | string> }) =>
|
||||||
|
apiRequest.put(`/cashflow/transactions/unexclude`, { ids: value.ids }),
|
||||||
|
{
|
||||||
|
onSuccess: (res, id) => {
|
||||||
|
onValidateExcludeUncategorizedTransaction(queryClient);
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ interface StorePlaidState {
|
|||||||
openMatchingTransactionAside: boolean;
|
openMatchingTransactionAside: boolean;
|
||||||
uncategorizedTransactionIdForMatching: number | null;
|
uncategorizedTransactionIdForMatching: number | null;
|
||||||
openReconcileMatchingTransaction: { isOpen: boolean; pending: number };
|
openReconcileMatchingTransaction: { isOpen: boolean; pending: number };
|
||||||
|
|
||||||
|
uncategorizedTransactionsSelected: Array<number | string>;
|
||||||
|
excludedTransactionsSelected: Array<number | string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PlaidSlice = createSlice({
|
export const PlaidSlice = createSlice({
|
||||||
@@ -17,6 +20,8 @@ export const PlaidSlice = createSlice({
|
|||||||
isOpen: false,
|
isOpen: false,
|
||||||
pending: 0,
|
pending: 0,
|
||||||
},
|
},
|
||||||
|
uncategorizedTransactionsSelected: [],
|
||||||
|
excludedTransactionsSelected: [],
|
||||||
} as StorePlaidState,
|
} as StorePlaidState,
|
||||||
reducers: {
|
reducers: {
|
||||||
setPlaidId: (state: StorePlaidState, action: PayloadAction<string>) => {
|
setPlaidId: (state: StorePlaidState, action: PayloadAction<string>) => {
|
||||||
@@ -52,6 +57,28 @@ export const PlaidSlice = createSlice({
|
|||||||
state.openReconcileMatchingTransaction.isOpen = false;
|
state.openReconcileMatchingTransaction.isOpen = false;
|
||||||
state.openReconcileMatchingTransaction.pending = 0;
|
state.openReconcileMatchingTransaction.pending = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setUncategorizedTransactionsSelected: (
|
||||||
|
state: StorePlaidState,
|
||||||
|
action: PayloadAction<{ transactionIds: Array<string | number> }>,
|
||||||
|
) => {
|
||||||
|
state.uncategorizedTransactionsSelected = action.payload.transactionIds;
|
||||||
|
},
|
||||||
|
|
||||||
|
resetUncategorizedTransactionsSelected: (state: StorePlaidState) => {
|
||||||
|
state.uncategorizedTransactionsSelected = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
setExcludedTransactionsSelected: (
|
||||||
|
state: StorePlaidState,
|
||||||
|
action: PayloadAction<{ ids: Array<string | number> }>,
|
||||||
|
) => {
|
||||||
|
state.excludedTransactionsSelected = action.payload.ids;
|
||||||
|
},
|
||||||
|
|
||||||
|
resetExcludedTransactionsSelected: (state: StorePlaidState) => {
|
||||||
|
state.excludedTransactionsSelected = [];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -62,6 +89,10 @@ export const {
|
|||||||
closeMatchingTransactionAside,
|
closeMatchingTransactionAside,
|
||||||
openReconcileMatchingTransaction,
|
openReconcileMatchingTransaction,
|
||||||
closeReconcileMatchingTransaction,
|
closeReconcileMatchingTransaction,
|
||||||
|
setUncategorizedTransactionsSelected,
|
||||||
|
resetUncategorizedTransactionsSelected,
|
||||||
|
setExcludedTransactionsSelected,
|
||||||
|
resetExcludedTransactionsSelected,
|
||||||
} = PlaidSlice.actions;
|
} = PlaidSlice.actions;
|
||||||
|
|
||||||
export const getPlaidToken = (state: any) => state.plaid.plaidToken;
|
export const getPlaidToken = (state: any) => state.plaid.plaidToken;
|
||||||
|
|||||||
Reference in New Issue
Block a user