mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 15:20:34 +00:00
feat: delete uncategorized transactions before deleting bank account
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable global-require */
|
/* eslint-disable global-require */
|
||||||
import * as R from 'ramda';
|
import { Model, mixin } from 'objection';
|
||||||
import { Model, ModelOptions, QueryContext, mixin } from 'objection';
|
|
||||||
import TenantModel from 'models/TenantModel';
|
import TenantModel from 'models/TenantModel';
|
||||||
import ModelSettings from './ModelSetting';
|
import ModelSettings from './ModelSetting';
|
||||||
import Account from './Account';
|
import Account from './Account';
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ export class AccountsApplication {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new account.
|
* Creates a new account.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {IAccountCreateDTO} accountDTO
|
* @param {IAccountCreateDTO} accountDTO
|
||||||
* @returns {Promise<IAccount>}
|
* @returns {Promise<IAccount>}
|
||||||
*/
|
*/
|
||||||
public createAccount = (
|
public createAccount = (
|
||||||
@@ -108,8 +108,8 @@ export class AccountsApplication {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the account details.
|
* Retrieves the account details.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {number} accountId
|
* @param {number} accountId
|
||||||
* @returns {Promise<IAccount>}
|
* @returns {Promise<IAccount>}
|
||||||
*/
|
*/
|
||||||
public getAccount = (tenantId: number, accountId: number) => {
|
public getAccount = (tenantId: number, accountId: number) => {
|
||||||
|
|||||||
@@ -23,46 +23,56 @@ export class DeleteUncategorizedTransactionsOnAccountDeleting {
|
|||||||
public attach(bus) {
|
public attach(bus) {
|
||||||
bus.subscribe(
|
bus.subscribe(
|
||||||
events.accounts.onDelete,
|
events.accounts.onDelete,
|
||||||
this.handleDeleteBankRulesOnAccountDeleting.bind(this),
|
this.handleDeleteBankRulesOnAccountDeleting.bind(this)
|
||||||
)
|
|
||||||
bus.subscribe(
|
|
||||||
events.accounts.onDelete,
|
|
||||||
this.handleDeleteUncategorizedTransactions.bind(this)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles delete the uncategorized transactions.
|
|
||||||
* @param {IAccountEventDeletePayload} payload -
|
|
||||||
*/
|
|
||||||
private async handleDeleteUncategorizedTransactions({ tenantId, oldAccount, trx }: IAccountEventDeletePayload) {
|
|
||||||
const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId);
|
|
||||||
|
|
||||||
await UncategorizedCashflowTransaction.query(trx)
|
|
||||||
.where('accountId', oldAccount.id)
|
|
||||||
.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles revert the recognized transactions and delete all the bank rules
|
* Handles revert the recognized transactions and delete all the bank rules
|
||||||
* associated to the deleted bank account.
|
* associated to the deleted bank account.
|
||||||
* @param {IAccountEventDeletePayload}
|
* @param {IAccountEventDeletePayload}
|
||||||
*/
|
*/
|
||||||
private async handleDeleteBankRulesOnAccountDeleting({ tenantId, oldAccount, trx }: IAccountEventDeletePayload) {
|
private async handleDeleteBankRulesOnAccountDeleting({
|
||||||
|
tenantId,
|
||||||
|
oldAccount,
|
||||||
|
trx,
|
||||||
|
}: IAccountEventDeletePayload) {
|
||||||
const knex = this.tenancy.knex(tenantId);
|
const knex = this.tenancy.knex(tenantId);
|
||||||
const { BankRule, UncategorizedCashflowTransaction, MatchedBankTransaction, RecognizedBankTransaction } = this.tenancy.models(tenantId);
|
const {
|
||||||
|
BankRule,
|
||||||
|
UncategorizedCashflowTransaction,
|
||||||
|
MatchedBankTransaction,
|
||||||
|
RecognizedBankTransaction,
|
||||||
|
} = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
const foundAssociatedRules = await BankRule.query(trx).where('applyIfAccountId', oldAccount.id);
|
const foundAssociatedRules = await BankRule.query(trx).where(
|
||||||
const foundAssociatedRulesIds = foundAssociatedRules.map(rule => rule.id);
|
'applyIfAccountId',
|
||||||
|
oldAccount.id
|
||||||
|
);
|
||||||
|
const foundAssociatedRulesIds = foundAssociatedRules.map((rule) => rule.id);
|
||||||
|
|
||||||
await initialize(knex, [
|
await initialize(knex, [
|
||||||
UncategorizedCashflowTransaction,
|
UncategorizedCashflowTransaction,
|
||||||
RecognizedBankTransaction,
|
RecognizedBankTransaction,
|
||||||
MatchedBankTransaction,
|
MatchedBankTransaction,
|
||||||
]);
|
]);
|
||||||
|
// Revert the recognized transactions of the given bank rules.
|
||||||
|
await this.revertRecognizedTransactins.revertRecognizedTransactions(
|
||||||
|
tenantId,
|
||||||
|
foundAssociatedRulesIds,
|
||||||
|
null,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
// Delete the associated uncategorized transactions.
|
||||||
|
await UncategorizedCashflowTransaction.query(trx)
|
||||||
|
.where('accountId', oldAccount.id)
|
||||||
|
.delete();
|
||||||
|
|
||||||
await this.revertRecognizedTransactins.revertRecognizedTransactions(tenantId, foundAssociatedRulesIds, null, trx)
|
// Delete the given bank rules.
|
||||||
|
await this.deleteBankRules.deleteBankRules(
|
||||||
await this.deleteBankRules.deleteBankRules(tenantId, foundAssociatedRulesIds);
|
tenantId,
|
||||||
|
foundAssociatedRulesIds,
|
||||||
|
trx
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { Inject, Service } from 'typedi';
|
|||||||
import { IAccountEventDeletedPayload } from '@/interfaces';
|
import { IAccountEventDeletedPayload } from '@/interfaces';
|
||||||
import { PlaidClientWrapper } from '@/lib/Plaid';
|
import { PlaidClientWrapper } from '@/lib/Plaid';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import { runAfterTransaction } from '@/services/UnitOfWork/TransactionsHooks';
|
import { runAfterTransaction } from '@/services/UnitOfWork/TransactionsHooks';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class DisconnectPlaidItemOnAccountDeleted {
|
export class DisconnectPlaidItemOnAccountDeleted {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import { Knex } from 'knex';
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { PlaidClientWrapper } from '@/lib/Plaid/Plaid';
|
import { PlaidClientWrapper } from '@/lib/Plaid/Plaid';
|
||||||
import { PlaidSyncDb } from './PlaidSyncDB';
|
import { PlaidSyncDb } from './PlaidSyncDB';
|
||||||
import { PlaidFetchedTransactionsUpdates } from '@/interfaces';
|
import { PlaidFetchedTransactionsUpdates } from '@/interfaces';
|
||||||
import UnitOfWork from '@/services/UnitOfWork';
|
import UnitOfWork from '@/services/UnitOfWork';
|
||||||
import { Knex } from 'knex';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class PlaidUpdateTransactions {
|
export class PlaidUpdateTransactions {
|
||||||
@@ -19,9 +19,9 @@ export class PlaidUpdateTransactions {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles sync the Plaid item to Bigcaptial under UOW.
|
* Handles sync the Plaid item to Bigcaptial under UOW.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId - Tenant id.
|
||||||
* @param {number} plaidItemId
|
* @param {number} plaidItemId - Plaid item id.
|
||||||
* @returns {Promise<{ addedCount: number; modifiedCount: number; removedCount: number; }>}
|
* @returns {Promise<{ addedCount: number; modifiedCount: number; removedCount: number; }>}
|
||||||
*/
|
*/
|
||||||
public async updateTransactions(tenantId: number, plaidItemId: string) {
|
public async updateTransactions(tenantId: number, plaidItemId: string) {
|
||||||
return this.uow.withTransaction(tenantId, (trx: Knex.Transaction) => {
|
return this.uow.withTransaction(tenantId, (trx: Knex.Transaction) => {
|
||||||
|
|||||||
@@ -26,31 +26,39 @@ export class DeleteBankRuleSerivce {
|
|||||||
* @param {number} ruleId
|
* @param {number} ruleId
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public async deleteBankRule(tenantId: number, ruleId: number, trx?: Knex.Transaction): Promise<void> {
|
public async deleteBankRule(
|
||||||
|
tenantId: number,
|
||||||
|
ruleId: number,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
): Promise<void> {
|
||||||
const { BankRule, BankRuleCondition } = this.tenancy.models(tenantId);
|
const { BankRule, BankRuleCondition } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
const oldBankRule = await BankRule.query()
|
const oldBankRule = await BankRule.query()
|
||||||
.findById(ruleId)
|
.findById(ruleId)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
return this.uow.withTransaction(
|
||||||
// Triggers `onBankRuleDeleting` event.
|
tenantId,
|
||||||
await this.eventPublisher.emitAsync(events.bankRules.onDeleting, {
|
async (trx: Knex.Transaction) => {
|
||||||
tenantId,
|
// Triggers `onBankRuleDeleting` event.
|
||||||
oldBankRule,
|
await this.eventPublisher.emitAsync(events.bankRules.onDeleting, {
|
||||||
ruleId,
|
tenantId,
|
||||||
trx,
|
oldBankRule,
|
||||||
} as IBankRuleEventDeletingPayload);
|
ruleId,
|
||||||
|
trx,
|
||||||
|
} as IBankRuleEventDeletingPayload);
|
||||||
|
|
||||||
await BankRuleCondition.query(trx).where('ruleId', ruleId).delete();
|
await BankRuleCondition.query(trx).where('ruleId', ruleId).delete()
|
||||||
await BankRule.query(trx).findById(ruleId).delete();
|
await BankRule.query(trx).findById(ruleId).delete();
|
||||||
|
|
||||||
// Triggers `onBankRuleDeleted` event.
|
// Triggers `onBankRuleDeleted` event.
|
||||||
await await this.eventPublisher.emitAsync(events.bankRules.onDeleted, {
|
await await this.eventPublisher.emitAsync(events.bankRules.onDeleted, {
|
||||||
tenantId,
|
tenantId,
|
||||||
ruleId,
|
ruleId,
|
||||||
trx,
|
trx,
|
||||||
} as IBankRuleEventDeletedPayload);
|
} as IBankRuleEventDeletedPayload);
|
||||||
}, trx);
|
},
|
||||||
|
trx
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { Inject, Service } from "typedi";
|
import { Inject, Service } from 'typedi';
|
||||||
import PromisePool from "@supercharge/promise-pool";
|
import PromisePool from '@supercharge/promise-pool';
|
||||||
import { castArray, uniq } from "lodash";
|
import { castArray, uniq } from 'lodash';
|
||||||
import { DeleteBankRuleSerivce } from "./DeleteBankRule";
|
import { DeleteBankRuleSerivce } from './DeleteBankRule';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class DeleteBankRulesService {
|
export class DeleteBankRulesService {
|
||||||
@@ -11,16 +11,24 @@ export class DeleteBankRulesService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete bank rules.
|
* Delete bank rules.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {number | Array<number>} bankRuleId
|
* @param {number | Array<number>} bankRuleId
|
||||||
*/
|
*/
|
||||||
async deleteBankRules(tenantId: number, bankRuleId: number | Array<number>, trx?: Knex.Transaction) {
|
async deleteBankRules(
|
||||||
|
tenantId: number,
|
||||||
|
bankRuleId: number | Array<number>,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
) {
|
||||||
const bankRulesIds = uniq(castArray(bankRuleId));
|
const bankRulesIds = uniq(castArray(bankRuleId));
|
||||||
|
|
||||||
await PromisePool.withConcurrency(1)
|
const results = await PromisePool.withConcurrency(1)
|
||||||
.for(bankRulesIds)
|
.for(bankRulesIds)
|
||||||
.process(async (bankRuleId: number) => {
|
.process(async (bankRuleId: number) => {
|
||||||
await this.deleteBankRuleService.deleteBankRule(tenantId, bankRuleId, trx);
|
await this.deleteBankRuleService.deleteBankRule(
|
||||||
|
tenantId,
|
||||||
|
bankRuleId,
|
||||||
|
trx
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,7 +208,6 @@ function AccountTransactionsActionsBar({
|
|||||||
bankAccountId: accountId,
|
bankAccountId: accountId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handles uncategorize the categorized transactions in bulk.
|
// Handles uncategorize the categorized transactions in bulk.
|
||||||
const handleUncategorizeCategorizedBulkBtnClick = () => {
|
const handleUncategorizeCategorizedBulkBtnClick = () => {
|
||||||
openAlert('uncategorize-transactions-bulk', {
|
openAlert('uncategorize-transactions-bulk', {
|
||||||
@@ -218,9 +217,9 @@ function AccountTransactionsActionsBar({
|
|||||||
// Handles the delete account button click.
|
// Handles the delete account button click.
|
||||||
const handleDeleteAccountClick = () => {
|
const handleDeleteAccountClick = () => {
|
||||||
openAlert('account-delete', {
|
openAlert('account-delete', {
|
||||||
accountId
|
accountId,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardActionsBar>
|
<DashboardActionsBar>
|
||||||
@@ -372,9 +371,17 @@ function AccountTransactionsActionsBar({
|
|||||||
|
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
<If condition={isSyncingOwner && isFeedsActive}>
|
<If condition={isSyncingOwner && isFeedsActive}>
|
||||||
<MenuItem intent={Intent.DANGER} onClick={handleDisconnectClick} text={'Disconnect'} />
|
<MenuItem
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
onClick={handleDisconnectClick}
|
||||||
|
text={'Disconnect'}
|
||||||
|
/>
|
||||||
</If>
|
</If>
|
||||||
<MenuItem intent={Intent.DANGER} onClick={handleDeleteAccountClick} text={'Delete'} />
|
<MenuItem
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
onClick={handleDeleteAccountClick}
|
||||||
|
text={'Delete'}
|
||||||
|
/>
|
||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { TABLES } from '@/constants/tables';
|
|||||||
import withSettings from '@/containers/Settings/withSettings';
|
import withSettings from '@/containers/Settings/withSettings';
|
||||||
import withAlertsActions from '@/containers/Alert/withAlertActions';
|
import withAlertsActions from '@/containers/Alert/withAlertActions';
|
||||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||||
|
import { withBankingActions } from '../withBankingActions';
|
||||||
|
|
||||||
import { useMemorizedColumnsWidths } from '@/hooks';
|
import { useMemorizedColumnsWidths } from '@/hooks';
|
||||||
import { useAccountTransactionsColumns, ActionsMenu } from './components';
|
import { useAccountTransactionsColumns, ActionsMenu } from './components';
|
||||||
@@ -26,7 +27,6 @@ import { useUncategorizeTransaction } from '@/hooks/query';
|
|||||||
import { handleCashFlowTransactionType } from './utils';
|
import { handleCashFlowTransactionType } from './utils';
|
||||||
|
|
||||||
import { compose } from '@/utils';
|
import { compose } from '@/utils';
|
||||||
import { withBankingActions } from '../withBankingActions';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Account transactions data table.
|
* Account transactions data table.
|
||||||
|
|||||||
Reference in New Issue
Block a user