From 1a01461f5db31e4fc724243f6a384afbf78cd872 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Mon, 29 Jul 2024 16:20:59 +0200 Subject: [PATCH] feat: delete Plaid item once bank account deleted --- packages/server/src/interfaces/Account.ts | 1 + packages/server/src/loaders/eventEmitter.ts | 2 + .../BankAccounts/DisconnectBankAccount.tsx | 4 +- .../DisconnectPlaidItemOnAccountDeleted.ts | 59 +++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts diff --git a/packages/server/src/interfaces/Account.ts b/packages/server/src/interfaces/Account.ts index b1a880b80..29c75a44e 100644 --- a/packages/server/src/interfaces/Account.ts +++ b/packages/server/src/interfaces/Account.ts @@ -37,6 +37,7 @@ export interface IAccount { accountNormal: string; accountParentType: string; bankBalance: string; + plaidItemId: number | null } export enum AccountNormal { diff --git a/packages/server/src/loaders/eventEmitter.ts b/packages/server/src/loaders/eventEmitter.ts index 8595bed55..9d7e24905 100644 --- a/packages/server/src/loaders/eventEmitter.ts +++ b/packages/server/src/loaders/eventEmitter.ts @@ -113,6 +113,7 @@ import { UnlinkBankRuleOnDeleteBankRule } from '@/services/Banking/Rules/events/ import { DecrementUncategorizedTransactionOnMatching } from '@/services/Banking/Matching/events/DecrementUncategorizedTransactionsOnMatch'; import { DecrementUncategorizedTransactionOnExclude } from '@/services/Banking/Exclude/events/DecrementUncategorizedTransactionOnExclude'; import { DecrementUncategorizedTransactionOnCategorize } from '@/services/Cashflow/subscribers/DecrementUncategorizedTransactionOnCategorize'; +import { DisconnectPlaidItemOnAccountDeleted } from '@/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted'; export default () => { return new EventPublisher(); @@ -274,5 +275,6 @@ export const susbcribers = () => { // Plaid RecognizeSyncedBankTranasctions, + DisconnectPlaidItemOnAccountDeleted, ]; }; diff --git a/packages/server/src/services/Banking/BankAccounts/DisconnectBankAccount.tsx b/packages/server/src/services/Banking/BankAccounts/DisconnectBankAccount.tsx index d562b31fc..3169e47ac 100644 --- a/packages/server/src/services/Banking/BankAccounts/DisconnectBankAccount.tsx +++ b/packages/server/src/services/Banking/BankAccounts/DisconnectBankAccount.tsx @@ -25,7 +25,7 @@ export class DisconnectBankAccount { * @param {number} bankAccountId * @returns {Promise} */ - async disconnectBankAccount(tenantId: number, bankAccountId: number) { + public async disconnectBankAccount(tenantId: number, bankAccountId: number) { const { Account, PlaidItem } = this.tenancy.models(tenantId); // Retrieve the bank account or throw not found error. @@ -57,7 +57,7 @@ export class DisconnectBankAccount { isFeedsActive: false, }); // Remove the Plaid item. - const data = await plaidInstance.itemRemove({ + await plaidInstance.itemRemove({ access_token: oldPlaidItem.plaidAccessToken, }); // Triggers `onBankAccountDisconnected` event. diff --git a/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts b/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts new file mode 100644 index 000000000..958fa0bb3 --- /dev/null +++ b/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts @@ -0,0 +1,59 @@ +import { IAccountEventDeletedPayload } from '@/interfaces'; +import { PlaidClientWrapper } from '@/lib/Plaid'; +import HasTenancyService from '@/services/Tenancy/TenancyService'; +import events from '@/subscribers/events'; +import { Inject, Service } from 'typedi'; + +@Service() +export class DisconnectPlaidItemOnAccountDeleted { + @Inject() + private tenancy: HasTenancyService; + + /** + * Constructor method. + */ + public attach(bus) { + bus.subscribe( + events.accounts.onDeleted, + this.handleDisconnectPlaidItemOnAccountDelete.bind(this) + ); + } + + /** + * Deletes Plaid item from the system and Plaid once the account deleted. + * @param {IAccountEventDeletedPayload} payload + * @returns {Promise} + */ + private async handleDisconnectPlaidItemOnAccountDelete({ + tenantId, + oldAccount, + trx, + }: IAccountEventDeletedPayload) { + const { PlaidItem, Account } = this.tenancy.models(tenantId); + + // Can't continue if the deleted account is not linked to Plaid item. + if (!oldAccount.plaidItemId) return; + + // Retrieves the Plaid item that associated to the deleted account. + const oldPlaidItem = await PlaidItem.query(trx).findById( + oldAccount.plaidItemId + ); + // Unlink the Plaid item from all account before deleting it. + await Account.query(trx) + .where('plaidItemId', oldAccount.plaidItemId) + .patch({ + plaidItemId: null, + }); + // Remove the Plaid item from the system. + await PlaidItem.query(trx).findById(oldAccount.plaidItemId).delete(); + + if (oldPlaidItem) { + const plaidInstance = new PlaidClientWrapper(); + + // Remove the Plaid item. + await plaidInstance.itemRemove({ + access_token: oldPlaidItem.plaidAccessToken, + }); + } + } +}