feat: pause bank account feeds

This commit is contained in:
Ahmed Bouhuolia
2024-08-04 21:14:05 +02:00
parent fc0240c692
commit f9cf6d325a
8 changed files with 64 additions and 13 deletions

View File

@@ -127,7 +127,7 @@ export class BankAccountsController extends BaseController {
} }
/** /**
* * Resumes the bank account feeds sync.
* @param {Request} req * @param {Request} req
* @param {Response} res * @param {Response} res
* @param {NextFunction} next * @param {NextFunction} next
@@ -154,7 +154,7 @@ export class BankAccountsController extends BaseController {
} }
/** /**
* * Pauses the bank account feeds sync.
* @param {Request} req * @param {Request} req
* @param {Response} res * @param {Response} res
* @param {NextFunction} next * @param {NextFunction} next

View File

@@ -18,9 +18,18 @@ export class AccountTransformer extends Transformer {
'flattenName', 'flattenName',
'bankBalanceFormatted', 'bankBalanceFormatted',
'lastFeedsUpdatedAtFormatted', 'lastFeedsUpdatedAtFormatted',
'isFeedsPaused',
]; ];
}; };
/**
* Exclude attributes.
* @returns {string[]}
*/
public excludeAttributes = (): string[] => {
return ['plaidItem'];
};
/** /**
* Retrieves the flatten name with all dependants accounts names. * Retrieves the flatten name with all dependants accounts names.
* @param {IAccount} account - * @param {IAccount} account -
@@ -66,6 +75,15 @@ export class AccountTransformer extends Transformer {
return this.formatDate(account.lastFeedsUpdatedAt); return this.formatDate(account.lastFeedsUpdatedAt);
}; };
/**
* Detarmines whether the bank account connection is paused.
* @param account
* @returns {boolean}
*/
protected isFeedsPaused = (account: any): boolean => {
return account.plaidItem?.isPaused || false;
};
/** /**
* Transformes the accounts collection to flat or nested array. * Transformes the accounts collection to flat or nested array.
* @param {IAccount[]} * @param {IAccount[]}

View File

@@ -25,7 +25,10 @@ export class GetAccount {
const { accountRepository } = this.tenancy.repositories(tenantId); const { accountRepository } = this.tenancy.repositories(tenantId);
// Find the given account or throw not found error. // Find the given account or throw not found error.
const account = await Account.query().findById(accountId).throwIfNotFound(); const account = await Account.query()
.findById(accountId)
.withGraphFetched('plaidItem')
.throwIfNotFound();
const accountsGraph = await accountRepository.getDependencyGraph(); const accountsGraph = await accountRepository.getDependencyGraph();

View File

@@ -37,7 +37,7 @@ export class PauseBankAccountFeeds {
} }
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => { return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
await PlaidItem.query(trx).findById(oldAccount.plaidItem.id).patch({ await PlaidItem.query(trx).findById(oldAccount.plaidItem.id).patch({
pausedAt: null, pausedAt: new Date(),
}); });
}); });
} }

View File

@@ -36,7 +36,7 @@ export class ResumeBankAccountFeeds {
} }
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => { return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
await PlaidItem.query(trx).findById(oldAccount.plaidItem.id).patch({ await PlaidItem.query(trx).findById(oldAccount.plaidItem.id).patch({
pausedAt: new Date(), pausedAt: null,
}); });
}); });
} }

View File

@@ -1,11 +1,15 @@
import { Inject, Service } from 'typedi'; import { Inject, Service } from 'typedi';
import { PlaidUpdateTransactions } from './PlaidUpdateTransactions'; import { PlaidUpdateTransactions } from './PlaidUpdateTransactions';
import HasTenancyService from '@/services/Tenancy/TenancyService';
@Service() @Service()
export class PlaidWebooks { export class PlaidWebooks {
@Inject() @Inject()
private updateTransactionsService: PlaidUpdateTransactions; private updateTransactionsService: PlaidUpdateTransactions;
@Inject()
private tenancy: HasTenancyService;
/** /**
* Listens to Plaid webhooks * Listens to Plaid webhooks
* @param {number} tenantId - Tenant Id. * @param {number} tenantId - Tenant Id.
@@ -61,7 +65,7 @@ export class PlaidWebooks {
plaidItemId: string plaidItemId: string
): void { ): void {
console.log( console.log(
`WEBHOOK: TRANSACTIONS: ${webhookCode}: Plaid_item_id ${plaidItemId}: ${additionalInfo}` `PLAID WEBHOOK: TRANSACTIONS: ${webhookCode}: Plaid_item_id ${plaidItemId}: ${additionalInfo}`
); );
} }
@@ -78,8 +82,21 @@ export class PlaidWebooks {
plaidItemId: string, plaidItemId: string,
webhookCode: string webhookCode: string
): Promise<void> { ): Promise<void> {
const { PlaidItem } = this.tenancy.models(tenantId);
const plaidItem = await PlaidItem.query()
.findById(plaidItemId)
.throwIfNotFound();
switch (webhookCode) { switch (webhookCode) {
case 'SYNC_UPDATES_AVAILABLE': { case 'SYNC_UPDATES_AVAILABLE': {
if (plaidItem.isPaused) {
this.serverLogAndEmitSocket(
'Plaid item syncing is paused.',
webhookCode,
plaidItemId
);
return;
}
// Fired when new transactions data becomes available. // Fired when new transactions data becomes available.
const { addedCount, modifiedCount, removedCount } = const { addedCount, modifiedCount, removedCount } =
await this.updateTransactionsService.updateTransactions( await this.updateTransactionsService.updateTransactions(

View File

@@ -78,6 +78,7 @@ function AccountTransactionsActionsBar({
const addMoneyOutOptions = useMemo(() => getAddMoneyOutOptions(), []); const addMoneyOutOptions = useMemo(() => getAddMoneyOutOptions(), []);
const isFeedsActive = !!currentAccount.is_feeds_active; const isFeedsActive = !!currentAccount.is_feeds_active;
const isFeedsPaused = currentAccount.is_feeds_paused;
const isSyncingOwner = currentAccount.is_syncing_owner; const isSyncingOwner = currentAccount.is_syncing_owner;
// Handle table row size change. // Handle table row size change.
@@ -244,7 +245,9 @@ function AccountTransactionsActionsBar({
<Tooltip <Tooltip
content={ content={
isFeedsActive isFeedsActive
? 'The bank syncing is active' ? isFeedsPaused
? 'The bank syncing is paused'
: 'The bank syncing is active'
: 'The bank syncing is disconnected' : 'The bank syncing is disconnected'
} }
minimal={true} minimal={true}
@@ -253,7 +256,13 @@ function AccountTransactionsActionsBar({
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="feed" iconSize={16} />} icon={<Icon icon="feed" iconSize={16} />}
intent={isFeedsActive ? Intent.SUCCESS : Intent.DANGER} intent={
isFeedsActive
? isFeedsPaused
? Intent.WARNING
: Intent.SUCCESS
: Intent.DANGER
}
/> />
</Tooltip> </Tooltip>
</If> </If>
@@ -291,6 +300,11 @@ function AccountTransactionsActionsBar({
content={ content={
<Menu> <Menu>
<If condition={isSyncingOwner && isFeedsActive}> <If condition={isSyncingOwner && isFeedsActive}>
<MenuItem onClick={handleBankUpdateClick} text={'Update'} />
<MenuDivider />
</If>
<If condition={isSyncingOwner && isFeedsActive && !isFeedsPaused}>
<MenuItem <MenuItem
onClick={handlePauseFeedsSyncing} onClick={handlePauseFeedsSyncing}
text={'Pause bank feeds'} text={'Pause bank feeds'}
@@ -298,7 +312,7 @@ function AccountTransactionsActionsBar({
<MenuDivider /> <MenuDivider />
</If> </If>
<If condition={isSyncingOwner && isFeedsActive}> <If condition={isSyncingOwner && isFeedsActive && isFeedsPaused}>
<MenuItem <MenuItem
onClick={handleResumeFeedsSyncing} onClick={handleResumeFeedsSyncing}
text={'Resume bank feeds'} text={'Resume bank feeds'}
@@ -306,10 +320,6 @@ function AccountTransactionsActionsBar({
<MenuDivider /> <MenuDivider />
</If> </If>
<If condition={isSyncingOwner && isFeedsActive}>
<MenuItem onClick={handleBankUpdateClick} text={'Update'} />
<MenuDivider />
</If>
<MenuItem onClick={handleBankRulesClick} text={'Bank rules'} /> <MenuItem onClick={handleBankRulesClick} text={'Bank rules'} />
<If condition={isSyncingOwner && isFeedsActive}> <If condition={isSyncingOwner && isFeedsActive}>

View File

@@ -229,6 +229,9 @@ $dashboard-views-bar-height: 44px;
} }
} }
&.#{$ns}-minimal.#{$ns}-intent-warning{
color: #cc7e25;
}
&.button--blue-highlight { &.button--blue-highlight {
background-color: #ebfaff; background-color: #ebfaff;