From 1086166e04d46b1c848eb67b36ee959a277aa90e Mon Sep 17 00:00:00 2001 From: Abdalhamid Alhamad Date: Wed, 14 Jan 2026 16:31:47 +0300 Subject: [PATCH] feat: improve transaction notification listener for accurate balance retrieval - Enhanced TransactionNotificationListener to fetch updated balances for child and parent accounts by bypassing entity cache. - Implemented error handling and fallback mechanisms to ensure reliable balance notifications. - Updated logging for better traceability of balance fetching processes. --- .../transaction-notification.listener.ts | 114 +++++++++++++++--- 1 file changed, 95 insertions(+), 19 deletions(-) diff --git a/src/common/modules/notification/listeners/transaction-notification.listener.ts b/src/common/modules/notification/listeners/transaction-notification.listener.ts index c2e847a..7faec7e 100644 --- a/src/common/modules/notification/listeners/transaction-notification.listener.ts +++ b/src/common/modules/notification/listeners/transaction-notification.listener.ts @@ -105,11 +105,40 @@ export class TransactionNotificationListener { const amount = transaction.transactionAmount; const merchant = transaction.merchantName || 'merchant'; - // Reload card to get the latest account balance after transaction - const cardWithUpdatedBalance = await this.cardService.getCardById(card.id); - const balance = cardWithUpdatedBalance.account?.balance || card.account?.balance || 0; + // Fetch account by reference number to get fresh balance (bypasses entity cache) + // This ensures we get the updated balance after the transaction + let balance = 0; + let accountCurrency: string | undefined; - const accountCurrency = cardWithUpdatedBalance.account?.currency || card.account?.currency; + try { + // Reload card to get account reference + const cardWithUpdatedBalance = await this.cardService.getCardById(card.id); + if (cardWithUpdatedBalance?.account?.accountReference) { + // Fetch by reference number to get fresh balance from database + const account = await this.accountService.getAccountByReferenceNumber( + cardWithUpdatedBalance.account.accountReference + ); + balance = account.balance; + accountCurrency = account.currency; + this.logger.debug( + `[Child Notification] Fetched account by reference - balance: ${balance} ${accountCurrency}` + ); + } else { + // Fallback: use card's account balance + balance = cardWithUpdatedBalance.account?.balance || card.account?.balance || 0; + accountCurrency = cardWithUpdatedBalance.account?.currency || card.account?.currency; + this.logger.debug( + `[Child Notification] Using card account balance - balance: ${balance} ${accountCurrency}` + ); + } + } catch (error: any) { + this.logger.warn( + `[Child Notification] Could not fetch account by reference: ${error?.message}. Using card account balance.` + ); + // Fallback: use card's account balance + balance = card.account?.balance || 0; + accountCurrency = card.account?.currency; + } const currency = getCurrency( accountCurrency, transaction.transactionCurrency, @@ -332,39 +361,86 @@ export class TransactionNotificationListener { const childName = childUser?.firstName || defaultChildName; const amount = transaction.transactionAmount; - // Always reload parent account to get updated balance after transfer - let parentAccount: any = null; + // Fetch parent account by reference number to get fresh balance (bypasses entity cache) + // This ensures we get the updated balance after the transfer + let parentAccountBalance = 0; + let parentAccountCurrency: string | undefined; + if (card.parentId) { try { - parentAccount = await this.accountService.getAccountByCustomerId(card.parentId); - this.logger.debug(`Fetched parent account for top-up notification - balance: ${parentAccount.balance}, currency: ${parentAccount.currency}`); + // Get parent's card to access their account reference + const parentCard = await this.cardService.getCardByCustomerId(card.parentId); + if (parentCard?.account?.accountReference) { + // Fetch by reference number to get fresh balance from database + const parentAccount = await this.accountService.getAccountByReferenceNumber( + parentCard.account.accountReference + ); + parentAccountBalance = parentAccount.balance; + parentAccountCurrency = parentAccount.currency; + this.logger.debug( + `[Parent Top-Up] Fetched parent account by reference - balance: ${parentAccountBalance} ${parentAccountCurrency}` + ); + } else { + // Fallback: try by customer ID + const parentAccount = await this.accountService.getAccountByCustomerId(card.parentId); + parentAccountBalance = parentAccount.balance; + parentAccountCurrency = parentAccount.currency; + this.logger.debug( + `[Parent Top-Up] Fetched parent account by customer ID - balance: ${parentAccountBalance} ${parentAccountCurrency}` + ); + } } catch (error: any) { - this.logger.warn(`Could not fetch parent account for customer ${card.parentId}: ${error?.message}, using child account balance`); + this.logger.warn( + `[Parent Top-Up] Could not fetch parent account for customer ${card.parentId}: ${error?.message}. Using child account balance as fallback.` + ); + parentAccountBalance = card.account?.balance || 0; + parentAccountCurrency = card.account?.currency; } - } - - // If parent account not found, try via customer relation - if (!parentAccount) { + } else { + // If no parentId, try via customer relation const parentCustomer = customer?.junior?.guardian?.customer; if (parentCustomer?.id) { try { - parentAccount = await this.accountService.getAccountByCustomerId(parentCustomer.id); - this.logger.debug(`Fetched parent account via customer relation - balance: ${parentAccount.balance}, currency: ${parentAccount.currency}`); + const parentCard = await this.cardService.getCardByCustomerId(parentCustomer.id); + if (parentCard?.account?.accountReference) { + const parentAccount = await this.accountService.getAccountByReferenceNumber( + parentCard.account.accountReference + ); + parentAccountBalance = parentAccount.balance; + parentAccountCurrency = parentAccount.currency; + this.logger.debug( + `[Parent Top-Up] Fetched parent account via customer relation (by reference) - balance: ${parentAccountBalance} ${parentAccountCurrency}` + ); + } else { + const parentAccount = await this.accountService.getAccountByCustomerId(parentCustomer.id); + parentAccountBalance = parentAccount.balance; + parentAccountCurrency = parentAccount.currency; + this.logger.debug( + `[Parent Top-Up] Fetched parent account via customer relation - balance: ${parentAccountBalance} ${parentAccountCurrency}` + ); + } } catch (error: any) { - this.logger.warn(`Could not fetch parent account via customer: ${error?.message}`); + this.logger.warn( + `[Parent Top-Up] Could not fetch parent account via customer: ${error?.message}. Using child account balance as fallback.` + ); + parentAccountBalance = card.account?.balance || 0; + parentAccountCurrency = card.account?.currency; } + } else { + parentAccountBalance = card.account?.balance || 0; + parentAccountCurrency = card.account?.currency; } } - const balance = parentAccount?.balance || card.account?.balance || 0; - const accountCurrency = parentAccount?.currency || card.account?.currency; + const balance = parentAccountBalance; + const accountCurrency = parentAccountCurrency; const currency = getCurrency( accountCurrency, transaction.transactionCurrency, ); this.logger.debug( - `[Parent Top-Up Notification] Parent account currency: ${parentAccount?.currency}, Account currency: ${accountCurrency}, Transaction currency: ${transaction.transactionCurrency}, Final currency: ${currency}, Parent balance: ${balance}, Amount: ${amount}` + `[Parent Top-Up Notification] Parent account currency: ${parentAccountCurrency}, Account currency: ${accountCurrency}, Transaction currency: ${transaction.transactionCurrency}, Final currency: ${currency}, Parent balance: ${balance}, Amount: ${amount}` ); const formattedAmount = formatCurrencyAmount(amount, currency);