From fb8118bea8c934361a79a347ac9498452568f0b6 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sun, 18 Aug 2024 14:20:23 +0200 Subject: [PATCH 1/3] fix: Delete bank account with uncategorized transactions --- packages/server/src/loaders/eventEmitter.ts | 2 + packages/server/src/models/Pagination.ts | 14 +++- .../src/services/Accounts/DeleteAccount.ts | 1 + ...ategorizedTransactionsOnAccountDeleting.ts | 68 +++++++++++++++++++ .../DisconnectPlaidItemOnAccountDeleted.ts | 19 ++++-- .../services/Banking/Rules/DeleteBankRule.ts | 4 +- .../services/Banking/Rules/DeleteBankRules.ts | 26 +++++++ .../AccountTransactionsActionsBar.tsx | 10 ++- 8 files changed, 131 insertions(+), 13 deletions(-) create mode 100644 packages/server/src/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting.ts create mode 100644 packages/server/src/services/Banking/Rules/DeleteBankRules.ts diff --git a/packages/server/src/loaders/eventEmitter.ts b/packages/server/src/loaders/eventEmitter.ts index f303b45273..5a5b02d037 100644 --- a/packages/server/src/loaders/eventEmitter.ts +++ b/packages/server/src/loaders/eventEmitter.ts @@ -115,6 +115,7 @@ import { DecrementUncategorizedTransactionOnExclude } from '@/services/Banking/E import { DecrementUncategorizedTransactionOnCategorize } from '@/services/Cashflow/subscribers/DecrementUncategorizedTransactionOnCategorize'; import { DisconnectPlaidItemOnAccountDeleted } from '@/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted'; import { LoopsEventsSubscriber } from '@/services/Loops/LoopsEventsSubscriber'; +import { DeleteUncategorizedTransactionsOnAccountDeleting } from '@/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting'; export default () => { return new EventPublisher(); @@ -277,6 +278,7 @@ export const susbcribers = () => { // Plaid RecognizeSyncedBankTranasctions, DisconnectPlaidItemOnAccountDeleted, + DeleteUncategorizedTransactionsOnAccountDeleting, // Loops LoopsEventsSubscriber diff --git a/packages/server/src/models/Pagination.ts b/packages/server/src/models/Pagination.ts index 7d1b899211..378839eb79 100644 --- a/packages/server/src/models/Pagination.ts +++ b/packages/server/src/models/Pagination.ts @@ -1,4 +1,5 @@ import { Model } from 'objection'; +import { castArray, omit, pick } from 'lodash'; import { isEmpty } from 'lodash'; import { ServiceError } from '@/exceptions'; @@ -16,7 +17,10 @@ export default class PaginationQueryBuilder extends Model.QueryBuilder { }); } - queryAndThrowIfHasRelations = ({ type, message }) => { + queryAndThrowIfHasRelations = ({ type, message, excludeRelations = [], includedRelations = [] }) => { + const _excludeRelations = castArray(excludeRelations); + const _includedRelations = castArray(includedRelations); + const model = this.modelClass(); const modelRelations = Object.keys(model.relationMappings).filter( (relation) => @@ -25,9 +29,13 @@ export default class PaginationQueryBuilder extends Model.QueryBuilder { ) !== -1 ); const relations = model.secureDeleteRelations || modelRelations; + const filteredRelations = !isEmpty(_includedRelations) ? + relations.filter(r => _includedRelations.includes(r)) : + !isEmpty(_excludeRelations) ? relations.filter(r => !excludeRelations.includes(r)) : relations; + this.runAfter((model, query) => { - const nonEmptyRelations = relations.filter( + const nonEmptyRelations = filteredRelations.filter( (relation) => !isEmpty(model[relation]) ); if (nonEmptyRelations.length > 0) { @@ -36,7 +44,7 @@ export default class PaginationQueryBuilder extends Model.QueryBuilder { return model; }); return this.onBuild((query) => { - relations.forEach((relation) => { + filteredRelations.forEach((relation) => { query.withGraphFetched(`${relation}(selectId)`).modifiers({ selectId(builder) { builder.select('id'); diff --git a/packages/server/src/services/Accounts/DeleteAccount.ts b/packages/server/src/services/Accounts/DeleteAccount.ts index d8d499c58b..632c78f621 100644 --- a/packages/server/src/services/Accounts/DeleteAccount.ts +++ b/packages/server/src/services/Accounts/DeleteAccount.ts @@ -73,6 +73,7 @@ export class DeleteAccount { .throwIfNotFound() .queryAndThrowIfHasRelations({ type: ERRORS.ACCOUNT_HAS_ASSOCIATED_TRANSACTIONS, + excludeRelations: ['uncategorizedTransactions', 'plaidItem'] }); // Authorize before delete account. await this.authorize(tenantId, accountId, oldAccount); diff --git a/packages/server/src/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting.ts b/packages/server/src/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting.ts new file mode 100644 index 0000000000..2acefa3c41 --- /dev/null +++ b/packages/server/src/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting.ts @@ -0,0 +1,68 @@ +import { Inject, Service } from 'typedi'; +import { initialize } from 'objection'; +import HasTenancyService from '@/services/Tenancy/TenancyService'; +import events from '@/subscribers/events'; +import { IAccountEventDeletePayload } from '@/interfaces'; +import { DeleteBankRulesService } from '../../Rules/DeleteBankRules'; +import { RevertRecognizedTransactions } from '../../RegonizeTranasctions/RevertRecognizedTransactions'; + +@Service() +export class DeleteUncategorizedTransactionsOnAccountDeleting { + @Inject() + private tenancy: HasTenancyService; + + @Inject() + private deleteBankRules: DeleteBankRulesService; + + @Inject() + private revertRecognizedTransactins: RevertRecognizedTransactions; + + /** + * Constructor method. + */ + public attach(bus) { + bus.subscribe( + events.accounts.onDelete, + 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 + * associated to the deleted bank account. + * @param {IAccountEventDeletePayload} + */ + private async handleDeleteBankRulesOnAccountDeleting({ tenantId, oldAccount, trx }: IAccountEventDeletePayload) { + const knex = this.tenancy.knex(tenantId); + const { BankRule, UncategorizedCashflowTransaction, MatchedBankTransaction, RecognizedBankTransaction } = this.tenancy.models(tenantId); + + const foundAssociatedRules = await BankRule.query(trx).where('applyIfAccountId', oldAccount.id); + const foundAssociatedRulesIds = foundAssociatedRules.map(rule => rule.id); + + await initialize(knex, [ + UncategorizedCashflowTransaction, + RecognizedBankTransaction, + MatchedBankTransaction, + ]); + + await this.revertRecognizedTransactins.revertRecognizedTransactions(tenantId, foundAssociatedRulesIds, null, trx) + + await this.deleteBankRules.deleteBankRules(tenantId, foundAssociatedRulesIds); + } +} \ No newline at end of file diff --git a/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts b/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts index 89f02da572..e0db91294b 100644 --- a/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts +++ b/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts @@ -3,6 +3,7 @@ import { IAccountEventDeletedPayload } from '@/interfaces'; import { PlaidClientWrapper } from '@/lib/Plaid'; import HasTenancyService from '@/services/Tenancy/TenancyService'; import events from '@/subscribers/events'; +import { runAfterTransaction } from '@/services/UnitOfWork/TransactionsHooks'; @Service() export class DisconnectPlaidItemOnAccountDeleted { @@ -51,13 +52,17 @@ export class DisconnectPlaidItemOnAccountDeleted { .findOne('plaidItemId', oldAccount.plaidItemId) .delete(); - if (oldPlaidItem) { - const plaidInstance = PlaidClientWrapper.getClient(); + // Remove Plaid item once the transaction resolve. + runAfterTransaction(trx, async () => { + if (oldPlaidItem) { + const plaidInstance = PlaidClientWrapper.getClient(); + + // Remove the Plaid item. + await plaidInstance.itemRemove({ + access_token: oldPlaidItem.plaidAccessToken, + }); + } + }) - // Remove the Plaid item. - await plaidInstance.itemRemove({ - access_token: oldPlaidItem.plaidAccessToken, - }); - } } } diff --git a/packages/server/src/services/Banking/Rules/DeleteBankRule.ts b/packages/server/src/services/Banking/Rules/DeleteBankRule.ts index c02ab6686e..b93385d4a5 100644 --- a/packages/server/src/services/Banking/Rules/DeleteBankRule.ts +++ b/packages/server/src/services/Banking/Rules/DeleteBankRule.ts @@ -26,7 +26,7 @@ export class DeleteBankRuleSerivce { * @param {number} ruleId * @returns {Promise} */ - public async deleteBankRule(tenantId: number, ruleId: number): Promise { + public async deleteBankRule(tenantId: number, ruleId: number, trx?: Knex.Transaction): Promise { const { BankRule, BankRuleCondition } = this.tenancy.models(tenantId); const oldBankRule = await BankRule.query() @@ -51,6 +51,6 @@ export class DeleteBankRuleSerivce { ruleId, trx, } as IBankRuleEventDeletedPayload); - }); + }, trx); } } diff --git a/packages/server/src/services/Banking/Rules/DeleteBankRules.ts b/packages/server/src/services/Banking/Rules/DeleteBankRules.ts new file mode 100644 index 0000000000..f31c9316cf --- /dev/null +++ b/packages/server/src/services/Banking/Rules/DeleteBankRules.ts @@ -0,0 +1,26 @@ +import { Knex } from 'knex'; +import { Inject, Service } from "typedi"; +import PromisePool from "@supercharge/promise-pool"; +import { castArray, uniq } from "lodash"; +import { DeleteBankRuleSerivce } from "./DeleteBankRule"; + +@Service() +export class DeleteBankRulesService { + @Inject() + private deleteBankRuleService: DeleteBankRuleSerivce; + + /** + * Delete bank rules. + * @param {number} tenantId + * @param {number | Array} bankRuleId + */ + async deleteBankRules(tenantId: number, bankRuleId: number | Array, trx?: Knex.Transaction) { + const bankRulesIds = uniq(castArray(bankRuleId)); + + await PromisePool.withConcurrency(1) + .for(bankRulesIds) + .process(async (bankRuleId: number) => { + await this.deleteBankRuleService.deleteBankRule(tenantId, bankRuleId, trx); + }); + } +} \ No newline at end of file diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsActionsBar.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsActionsBar.tsx index 5048d2b541..84544f3329 100644 --- a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsActionsBar.tsx +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsActionsBar.tsx @@ -215,6 +215,12 @@ function AccountTransactionsActionsBar({ uncategorizeTransactionsIds: categorizedTransactionsSelected, }); }; + // Handles the delete account button click. + const handleDeleteAccountClick = () => { + openAlert('account-delete', { + accountId + }) + } return ( @@ -364,9 +370,11 @@ function AccountTransactionsActionsBar({ + - + + } > From 2f21107a4339c971cf77395e0192cde16a073b66 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sun, 18 Aug 2024 19:30:09 +0200 Subject: [PATCH 2/3] feat: delete uncategorized transactions before deleting bank account --- .../UncategorizedCashflowTransaction.ts | 3 +- .../services/Accounts/AccountsApplication.ts | 8 +-- ...ategorizedTransactionsOnAccountDeleting.ts | 60 +++++++++++-------- .../DisconnectPlaidItemOnAccountDeleted.ts | 2 +- .../Banking/Plaid/PlaidUpdateTransactions.ts | 10 ++-- .../services/Banking/Rules/DeleteBankRule.ts | 48 ++++++++------- .../services/Banking/Rules/DeleteBankRules.ts | 28 +++++---- .../AccountTransactionsActionsBar.tsx | 19 ++++-- .../AccountTransactionsDataTable.tsx | 2 +- 9 files changed, 106 insertions(+), 74 deletions(-) diff --git a/packages/server/src/models/UncategorizedCashflowTransaction.ts b/packages/server/src/models/UncategorizedCashflowTransaction.ts index fbb737ccb0..2b86c9a4a3 100644 --- a/packages/server/src/models/UncategorizedCashflowTransaction.ts +++ b/packages/server/src/models/UncategorizedCashflowTransaction.ts @@ -1,6 +1,5 @@ /* eslint-disable global-require */ -import * as R from 'ramda'; -import { Model, ModelOptions, QueryContext, mixin } from 'objection'; +import { Model, mixin } from 'objection'; import TenantModel from 'models/TenantModel'; import ModelSettings from './ModelSetting'; import Account from './Account'; diff --git a/packages/server/src/services/Accounts/AccountsApplication.ts b/packages/server/src/services/Accounts/AccountsApplication.ts index b90eb37e9f..8a06b410f5 100644 --- a/packages/server/src/services/Accounts/AccountsApplication.ts +++ b/packages/server/src/services/Accounts/AccountsApplication.ts @@ -43,8 +43,8 @@ export class AccountsApplication { /** * Creates a new account. - * @param {number} tenantId - * @param {IAccountCreateDTO} accountDTO + * @param {number} tenantId + * @param {IAccountCreateDTO} accountDTO * @returns {Promise} */ public createAccount = ( @@ -108,8 +108,8 @@ export class AccountsApplication { /** * Retrieves the account details. - * @param {number} tenantId - * @param {number} accountId + * @param {number} tenantId + * @param {number} accountId * @returns {Promise} */ public getAccount = (tenantId: number, accountId: number) => { diff --git a/packages/server/src/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting.ts b/packages/server/src/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting.ts index 2acefa3c41..f2d9202ee8 100644 --- a/packages/server/src/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting.ts +++ b/packages/server/src/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting.ts @@ -23,46 +23,56 @@ export class DeleteUncategorizedTransactionsOnAccountDeleting { public attach(bus) { bus.subscribe( events.accounts.onDelete, - this.handleDeleteBankRulesOnAccountDeleting.bind(this), - ) - bus.subscribe( - events.accounts.onDelete, - this.handleDeleteUncategorizedTransactions.bind(this) + this.handleDeleteBankRulesOnAccountDeleting.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 * associated to the deleted bank account. * @param {IAccountEventDeletePayload} */ - private async handleDeleteBankRulesOnAccountDeleting({ tenantId, oldAccount, trx }: IAccountEventDeletePayload) { + private async handleDeleteBankRulesOnAccountDeleting({ + tenantId, + oldAccount, + trx, + }: IAccountEventDeletePayload) { 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 foundAssociatedRulesIds = foundAssociatedRules.map(rule => rule.id); + const foundAssociatedRules = await BankRule.query(trx).where( + 'applyIfAccountId', + oldAccount.id + ); + const foundAssociatedRulesIds = foundAssociatedRules.map((rule) => rule.id); await initialize(knex, [ UncategorizedCashflowTransaction, RecognizedBankTransaction, 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) - - await this.deleteBankRules.deleteBankRules(tenantId, foundAssociatedRulesIds); + // Delete the given bank rules. + await this.deleteBankRules.deleteBankRules( + tenantId, + foundAssociatedRulesIds, + trx + ); } -} \ No newline at end of file +} diff --git a/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts b/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts index e0db91294b..dfba3b7a9e 100644 --- a/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts +++ b/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts @@ -2,8 +2,8 @@ import { Inject, Service } from 'typedi'; import { IAccountEventDeletedPayload } from '@/interfaces'; import { PlaidClientWrapper } from '@/lib/Plaid'; import HasTenancyService from '@/services/Tenancy/TenancyService'; -import events from '@/subscribers/events'; import { runAfterTransaction } from '@/services/UnitOfWork/TransactionsHooks'; +import events from '@/subscribers/events'; @Service() export class DisconnectPlaidItemOnAccountDeleted { diff --git a/packages/server/src/services/Banking/Plaid/PlaidUpdateTransactions.ts b/packages/server/src/services/Banking/Plaid/PlaidUpdateTransactions.ts index 547aac45d0..c987dd84f3 100644 --- a/packages/server/src/services/Banking/Plaid/PlaidUpdateTransactions.ts +++ b/packages/server/src/services/Banking/Plaid/PlaidUpdateTransactions.ts @@ -1,10 +1,10 @@ -import HasTenancyService from '@/services/Tenancy/TenancyService'; +import { Knex } from 'knex'; import { Inject, Service } from 'typedi'; +import HasTenancyService from '@/services/Tenancy/TenancyService'; import { PlaidClientWrapper } from '@/lib/Plaid/Plaid'; import { PlaidSyncDb } from './PlaidSyncDB'; import { PlaidFetchedTransactionsUpdates } from '@/interfaces'; import UnitOfWork from '@/services/UnitOfWork'; -import { Knex } from 'knex'; @Service() export class PlaidUpdateTransactions { @@ -19,9 +19,9 @@ export class PlaidUpdateTransactions { /** * Handles sync the Plaid item to Bigcaptial under UOW. - * @param {number} tenantId - * @param {number} plaidItemId - * @returns {Promise<{ addedCount: number; modifiedCount: number; removedCount: number; }>} + * @param {number} tenantId - Tenant id. + * @param {number} plaidItemId - Plaid item id. + * @returns {Promise<{ addedCount: number; modifiedCount: number; removedCount: number; }>} */ public async updateTransactions(tenantId: number, plaidItemId: string) { return this.uow.withTransaction(tenantId, (trx: Knex.Transaction) => { diff --git a/packages/server/src/services/Banking/Rules/DeleteBankRule.ts b/packages/server/src/services/Banking/Rules/DeleteBankRule.ts index b93385d4a5..3981059c96 100644 --- a/packages/server/src/services/Banking/Rules/DeleteBankRule.ts +++ b/packages/server/src/services/Banking/Rules/DeleteBankRule.ts @@ -26,31 +26,39 @@ export class DeleteBankRuleSerivce { * @param {number} ruleId * @returns {Promise} */ - public async deleteBankRule(tenantId: number, ruleId: number, trx?: Knex.Transaction): Promise { + public async deleteBankRule( + tenantId: number, + ruleId: number, + trx?: Knex.Transaction + ): Promise { const { BankRule, BankRuleCondition } = this.tenancy.models(tenantId); const oldBankRule = await BankRule.query() .findById(ruleId) .throwIfNotFound(); - return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => { - // Triggers `onBankRuleDeleting` event. - await this.eventPublisher.emitAsync(events.bankRules.onDeleting, { - tenantId, - oldBankRule, - ruleId, - trx, - } as IBankRuleEventDeletingPayload); - - await BankRuleCondition.query(trx).where('ruleId', ruleId).delete(); - await BankRule.query(trx).findById(ruleId).delete(); - - // Triggers `onBankRuleDeleted` event. - await await this.eventPublisher.emitAsync(events.bankRules.onDeleted, { - tenantId, - ruleId, - trx, - } as IBankRuleEventDeletedPayload); - }, trx); + return this.uow.withTransaction( + tenantId, + async (trx: Knex.Transaction) => { + // Triggers `onBankRuleDeleting` event. + await this.eventPublisher.emitAsync(events.bankRules.onDeleting, { + tenantId, + oldBankRule, + ruleId, + trx, + } as IBankRuleEventDeletingPayload); + + await BankRuleCondition.query(trx).where('ruleId', ruleId).delete() + await BankRule.query(trx).findById(ruleId).delete(); + + // Triggers `onBankRuleDeleted` event. + await await this.eventPublisher.emitAsync(events.bankRules.onDeleted, { + tenantId, + ruleId, + trx, + } as IBankRuleEventDeletedPayload); + }, + trx + ); } } diff --git a/packages/server/src/services/Banking/Rules/DeleteBankRules.ts b/packages/server/src/services/Banking/Rules/DeleteBankRules.ts index f31c9316cf..98d17e9cda 100644 --- a/packages/server/src/services/Banking/Rules/DeleteBankRules.ts +++ b/packages/server/src/services/Banking/Rules/DeleteBankRules.ts @@ -1,8 +1,8 @@ import { Knex } from 'knex'; -import { Inject, Service } from "typedi"; -import PromisePool from "@supercharge/promise-pool"; -import { castArray, uniq } from "lodash"; -import { DeleteBankRuleSerivce } from "./DeleteBankRule"; +import { Inject, Service } from 'typedi'; +import PromisePool from '@supercharge/promise-pool'; +import { castArray, uniq } from 'lodash'; +import { DeleteBankRuleSerivce } from './DeleteBankRule'; @Service() export class DeleteBankRulesService { @@ -11,16 +11,24 @@ export class DeleteBankRulesService { /** * Delete bank rules. - * @param {number} tenantId - * @param {number | Array} bankRuleId + * @param {number} tenantId + * @param {number | Array} bankRuleId */ - async deleteBankRules(tenantId: number, bankRuleId: number | Array, trx?: Knex.Transaction) { + async deleteBankRules( + tenantId: number, + bankRuleId: number | Array, + trx?: Knex.Transaction + ) { const bankRulesIds = uniq(castArray(bankRuleId)); - await PromisePool.withConcurrency(1) + const results = await PromisePool.withConcurrency(1) .for(bankRulesIds) .process(async (bankRuleId: number) => { - await this.deleteBankRuleService.deleteBankRule(tenantId, bankRuleId, trx); + await this.deleteBankRuleService.deleteBankRule( + tenantId, + bankRuleId, + trx + ); }); } -} \ No newline at end of file +} diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsActionsBar.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsActionsBar.tsx index 84544f3329..be05ce22e8 100644 --- a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsActionsBar.tsx +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsActionsBar.tsx @@ -208,7 +208,6 @@ function AccountTransactionsActionsBar({ bankAccountId: accountId, }); }; - // Handles uncategorize the categorized transactions in bulk. const handleUncategorizeCategorizedBulkBtnClick = () => { openAlert('uncategorize-transactions-bulk', { @@ -218,9 +217,9 @@ function AccountTransactionsActionsBar({ // Handles the delete account button click. const handleDeleteAccountClick = () => { openAlert('account-delete', { - accountId - }) - } + accountId, + }); + }; return ( @@ -372,9 +371,17 @@ function AccountTransactionsActionsBar({ - + - + } > diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDataTable.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDataTable.tsx index 51749c7781..3c090d69bc 100644 --- a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDataTable.tsx +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDataTable.tsx @@ -17,6 +17,7 @@ import { TABLES } from '@/constants/tables'; import withSettings from '@/containers/Settings/withSettings'; import withAlertsActions from '@/containers/Alert/withAlertActions'; import withDrawerActions from '@/containers/Drawer/withDrawerActions'; +import { withBankingActions } from '../withBankingActions'; import { useMemorizedColumnsWidths } from '@/hooks'; import { useAccountTransactionsColumns, ActionsMenu } from './components'; @@ -26,7 +27,6 @@ import { useUncategorizeTransaction } from '@/hooks/query'; import { handleCashFlowTransactionType } from './utils'; import { compose } from '@/utils'; -import { withBankingActions } from '../withBankingActions'; /** * Account transactions data table. From 06ea631732daa6e2885aff85e228c9115e9d30b2 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sun, 18 Aug 2024 19:38:15 +0200 Subject: [PATCH 3/3] fix: making pagination more readable --- packages/server/src/models/Pagination.ts | 22 ++++++++++++++----- .../DisconnectPlaidItemOnAccountDeleted.ts | 18 ++++++--------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/packages/server/src/models/Pagination.ts b/packages/server/src/models/Pagination.ts index 378839eb79..5d308e90e8 100644 --- a/packages/server/src/models/Pagination.ts +++ b/packages/server/src/models/Pagination.ts @@ -17,7 +17,12 @@ export default class PaginationQueryBuilder extends Model.QueryBuilder { }); } - queryAndThrowIfHasRelations = ({ type, message, excludeRelations = [], includedRelations = [] }) => { + queryAndThrowIfHasRelations = ({ + type, + message, + excludeRelations = [], + includedRelations = [], + }) => { const _excludeRelations = castArray(excludeRelations); const _includedRelations = castArray(includedRelations); @@ -29,10 +34,17 @@ export default class PaginationQueryBuilder extends Model.QueryBuilder { ) !== -1 ); const relations = model.secureDeleteRelations || modelRelations; - const filteredRelations = !isEmpty(_includedRelations) ? - relations.filter(r => _includedRelations.includes(r)) : - !isEmpty(_excludeRelations) ? relations.filter(r => !excludeRelations.includes(r)) : relations; - + const filteredByIncluded = relations.filter((r) => + _includedRelations.includes(r) + ); + const filteredByExcluded = relations.filter( + (r) => !excludeRelations.includes(r) + ); + const filteredRelations = !isEmpty(_includedRelations) + ? filteredByIncluded + : !isEmpty(_excludeRelations) + ? filteredByExcluded + : relations; this.runAfter((model, query) => { const nonEmptyRelations = filteredRelations.filter( diff --git a/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts b/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts index dfba3b7a9e..4ecf71e81c 100644 --- a/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts +++ b/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts @@ -2,7 +2,6 @@ import { Inject, Service } from 'typedi'; import { IAccountEventDeletedPayload } from '@/interfaces'; import { PlaidClientWrapper } from '@/lib/Plaid'; import HasTenancyService from '@/services/Tenancy/TenancyService'; -import { runAfterTransaction } from '@/services/UnitOfWork/TransactionsHooks'; import events from '@/subscribers/events'; @Service() @@ -53,16 +52,13 @@ export class DisconnectPlaidItemOnAccountDeleted { .delete(); // Remove Plaid item once the transaction resolve. - runAfterTransaction(trx, async () => { - if (oldPlaidItem) { - const plaidInstance = PlaidClientWrapper.getClient(); - - // Remove the Plaid item. - await plaidInstance.itemRemove({ - access_token: oldPlaidItem.plaidAccessToken, - }); - } - }) + if (oldPlaidItem) { + const plaidInstance = PlaidClientWrapper.getClient(); + // Remove the Plaid item. + await plaidInstance.itemRemove({ + access_token: oldPlaidItem.plaidAccessToken, + }); + } } }