diff --git a/src/common/modules/notification/enums/notification-scope.enum.ts b/src/common/modules/notification/enums/notification-scope.enum.ts index 13b300a..fdb43ee 100644 --- a/src/common/modules/notification/enums/notification-scope.enum.ts +++ b/src/common/modules/notification/enums/notification-scope.enum.ts @@ -6,10 +6,14 @@ export enum NotificationScope { OTP = 'OTP', USER_INVITED = 'USER_INVITED', - // Transaction notifications - Top-up + // Transaction notifications - Top-up (external funds) CHILD_TOP_UP = 'CHILD_TOP_UP', PARENT_TOP_UP_CONFIRMATION = 'PARENT_TOP_UP_CONFIRMATION', + // Transaction notifications - Internal Transfer (parent to child) + CHILD_INTERNAL_TRANSFER = 'CHILD_INTERNAL_TRANSFER', + PARENT_INTERNAL_TRANSFER = 'PARENT_INTERNAL_TRANSFER', + // Transaction notifications - Spending CHILD_SPENDING = 'CHILD_SPENDING', PARENT_SPENDING_ALERT = 'PARENT_SPENDING_ALERT', diff --git a/src/common/modules/notification/listeners/transaction-notification.listener.ts b/src/common/modules/notification/listeners/transaction-notification.listener.ts index b671bce..83e901f 100644 --- a/src/common/modules/notification/listeners/transaction-notification.listener.ts +++ b/src/common/modules/notification/listeners/transaction-notification.listener.ts @@ -97,34 +97,52 @@ export class TransactionNotificationListener { return; } - const scope = isTopUp - ? NotificationScope.CHILD_TOP_UP - : NotificationScope.CHILD_SPENDING; + // Determine scope: internal transfer (parent to child) vs external top-up + let scope: NotificationScope; + if (isTopUp) { + scope = isChildSpending + ? NotificationScope.CHILD_INTERNAL_TRANSFER // Parent transferring to child + : NotificationScope.CHILD_TOP_UP; // External top-up + } else { + scope = NotificationScope.CHILD_SPENDING; + } const locale = this.getUserLocale(user); const amount = transaction.transactionAmount; const merchant = transaction.merchantName || 'merchant'; - // For child top-up notifications, show card.limit (newAmount) instead of account balance - // card.limit represents the total spending limit on the card after the transfer + // For child notifications, show the child's account balance (available balance) let balance = 0; let accountCurrency: string | undefined; if (isTopUp) { - // For top-up: show card limit (newAmount from transfer response) + // For top-up: show child's account balance after the transfer try { - // Reload card to get updated limit - const cardWithUpdatedLimit = await this.cardService.getCardById(card.id); - balance = cardWithUpdatedLimit.limit || card.limit || 0; - accountCurrency = cardWithUpdatedLimit.account?.currency || card.account?.currency; - this.logger.debug( - `[Child Top-Up Notification] Using card limit (newAmount) - limit: ${balance} ${accountCurrency}` - ); + // Reload card to get account reference with fresh balance + 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 Top-Up 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 Top-Up Notification] Using card account balance - balance: ${balance} ${accountCurrency}` + ); + } } catch (error: any) { this.logger.warn( - `[Child Top-Up Notification] Could not reload card: ${error?.message}. Using card limit from event.` + `[Child Top-Up Notification] Could not fetch account by reference: ${error?.message}. Using card account balance.` ); - balance = card.limit || 0; + balance = card.account?.balance || 0; accountCurrency = card.account?.currency; } } else { @@ -176,27 +194,36 @@ export class TransactionNotificationListener { let message: string; try { - title = isTopUp - ? this.i18n.t('app.NOTIFICATION.CHILD_TOP_UP_TITLE', { lang: locale }) - : this.i18n.t('app.NOTIFICATION.CHILD_SPENDING_TITLE', { lang: locale }); - - message = isTopUp - ? this.i18n.t('app.NOTIFICATION.CHILD_TOP_UP_MESSAGE', { - lang: locale, - args: { - amount: formattedAmount, - currency: currency, - balance: formattedBalance, - }, - }) - : this.i18n.t('app.NOTIFICATION.CHILD_SPENDING_MESSAGE', { - lang: locale, - args: { - amount: formattedAmount, - currency: currency, - merchant: merchant, - }, - }); + if (isTopUp) { + // Internal transfer or external top-up + const titleKey = isChildSpending + ? 'app.NOTIFICATION.CHILD_INTERNAL_TRANSFER_TITLE' + : 'app.NOTIFICATION.CHILD_TOP_UP_TITLE'; + const messageKey = isChildSpending + ? 'app.NOTIFICATION.CHILD_INTERNAL_TRANSFER_MESSAGE' + : 'app.NOTIFICATION.CHILD_TOP_UP_MESSAGE'; + + title = this.i18n.t(titleKey, { lang: locale }); + message = this.i18n.t(messageKey, { + lang: locale, + args: { + amount: formattedAmount, + currency: currency, + balance: formattedBalance, + }, + }); + } else { + // Spending + title = this.i18n.t('app.NOTIFICATION.CHILD_SPENDING_TITLE', { lang: locale }); + message = this.i18n.t('app.NOTIFICATION.CHILD_SPENDING_MESSAGE', { + lang: locale, + args: { + amount: formattedAmount, + currency: currency, + merchant: merchant, + }, + }); + } } catch (i18nError: any) { console.error(`[TransactionNotificationListener] i18n error:`, i18nError); this.logger.error(`i18n translation failed: ${i18nError?.message}`, i18nError?.stack); @@ -411,7 +438,7 @@ export class TransactionNotificationListener { } /** - * Notify parent when they top up their child's card + * Notify parent when they transfer money to their child's card (internal transfer) * This is a confirmation notification for the parent */ private async notifyParentOfTopUp(transaction: Transaction, card: Card): Promise { @@ -537,8 +564,8 @@ export class TransactionNotificationListener { let message: string; try { - title = this.i18n.t('app.NOTIFICATION.PARENT_TOP_UP_TITLE', { lang: locale }); - message = this.i18n.t('app.NOTIFICATION.PARENT_TOP_UP_MESSAGE', { + title = this.i18n.t('app.NOTIFICATION.PARENT_INTERNAL_TRANSFER_TITLE', { lang: locale }); + message = this.i18n.t('app.NOTIFICATION.PARENT_INTERNAL_TRANSFER_MESSAGE', { lang: locale, args: { amount: formattedAmount, @@ -548,9 +575,9 @@ export class TransactionNotificationListener { }, }); } catch (i18nError: any) { - console.error(`[TransactionNotificationListener] i18n error in parent top-up:`, i18nError); + console.error(`[TransactionNotificationListener] i18n error in parent internal transfer:`, i18nError); this.logger.error(`i18n translation failed: ${i18nError?.message}`, i18nError?.stack); - title = 'Transfer Completed'; + title = 'Internal Transfer Completed'; message = `${formattedAmount} ${currency} has been transferred to ${childName}'s card. ${childName}'s balance is ${formattedBalance} ${currency}`; } @@ -558,7 +585,7 @@ export class TransactionNotificationListener { userId: parentUser.id, title, message, - scope: NotificationScope.PARENT_TOP_UP_CONFIRMATION, + scope: NotificationScope.PARENT_INTERNAL_TRANSFER, preferences: this.getUserPreferences(parentUser), data: { transactionId: transaction.id, diff --git a/src/i18n/ar/app.json b/src/i18n/ar/app.json index 9d8f147..74c0b69 100644 --- a/src/i18n/ar/app.json +++ b/src/i18n/ar/app.json @@ -112,17 +112,19 @@ "NOT_FOUND": "لم يتم العثور على البطاقة." }, "NOTIFICATION": { - "CHILD_TOP_UP_TITLE": "تم إضافة الأموال", - "CHILD_TOP_UP_MESSAGE": "تم إضافة {amount} {currency} إلى بطاقتك. الرصيد الإجمالي: {balance} {currency}", - "CHILD_SPENDING_TITLE": "تمت العملية بنجاح", - "CHILD_SPENDING_MESSAGE": "لقد أنفقت {amount} {currency} في {merchant}", - "PARENT_TOP_UP_TITLE": "اكتمل التحويل", - "PARENT_TOP_UP_MESSAGE": "تم تحويل {amount} {currency} إلى بطاقة {childName}. رصيد {childName} هو {balance} {currency}", - "PARENT_SPENDING_TITLE": "تنبيه الإنفاق", - "PARENT_SPENDING_MESSAGE": "أنفق {childName} {amount} {currency} في {merchant}. الرصيد المتبقي: {balance} {currency}", + "CHILD_TOP_UP_TITLE": "تم إضافة رصيد", + "CHILD_TOP_UP_MESSAGE": "تمت إضافة {amount} {currency} إلى بطاقتك. إجمالي الرصيد: {balance} {currency}", + "CHILD_INTERNAL_TRANSFER_TITLE": "تم إضافة رصيد", + "CHILD_INTERNAL_TRANSFER_MESSAGE": "تمت إضافة {amount} {currency} إلى بطاقتك. إجمالي الرصيد: {balance} {currency}", + "PARENT_INTERNAL_TRANSFER_TITLE": "اكتمل التحويل", + "PARENT_INTERNAL_TRANSFER_MESSAGE": "تم تحويل {amount} {currency} إلى بطاقة {childName}. رصيد {childName}: {balance} {currency}", + "CHILD_SPENDING_TITLE": "عملية شراء ناجحة", + "CHILD_SPENDING_MESSAGE": "قمت بإنفاق {amount} {currency} في {merchant}", + "PARENT_SPENDING_TITLE": "تنبيه صرف", + "PARENT_SPENDING_MESSAGE": "قام {childName} بإنفاق {amount} {currency} في {merchant}. الرصيد المتبقي: {balance} {currency}", "YOUR_CHILD": "طفلك", - "MONEY_REQUEST_CREATED_TITLE": "طلب مال", - "MONEY_REQUEST_CREATED_MESSAGE": "طلب {childName} مبلغ {amount} {currency} لـ {reason}.", + "MONEY_REQUEST_CREATED_TITLE": "طلب مبلغ مالي", + "MONEY_REQUEST_CREATED_MESSAGE": "طلب {childName} مبلغ {amount} {currency} لـ {reason}", "MONEY_REQUEST_APPROVED_TITLE": "تمت الموافقة على طلب المال", "MONEY_REQUEST_APPROVED_MESSAGE": "تمت الموافقة على طلبك بمبلغ {amount} {currency}. تمت إضافة المال إلى حسابك.", "MONEY_REQUEST_DECLINED_TITLE": "تم رفض طلب المال", diff --git a/src/i18n/en/app.json b/src/i18n/en/app.json index 46b8a24..0c7f696 100644 --- a/src/i18n/en/app.json +++ b/src/i18n/en/app.json @@ -113,10 +113,12 @@ "NOTIFICATION": { "CHILD_TOP_UP_TITLE": "Funds Credited", "CHILD_TOP_UP_MESSAGE": "{amount} {currency} has been added to your card. Total balance: {balance} {currency}", + "CHILD_INTERNAL_TRANSFER_TITLE": "Funds Credited", + "CHILD_INTERNAL_TRANSFER_MESSAGE": "{amount} {currency} has been added to your card. Total balance: {balance} {currency}", + "PARENT_INTERNAL_TRANSFER_TITLE": "Internal Transfer Completed", + "PARENT_INTERNAL_TRANSFER_MESSAGE": "{amount} {currency} has been transferred to {childName}'s card. {childName}'s balance is {balance} {currency}", "CHILD_SPENDING_TITLE": "Purchase Successful", "CHILD_SPENDING_MESSAGE": "You spent {amount} {currency} at {merchant}", - "PARENT_TOP_UP_TITLE": "Transfer Completed", - "PARENT_TOP_UP_MESSAGE": "{amount} {currency} has been transferred to {childName}'s card. {childName}'s balance is {balance} {currency}", "PARENT_SPENDING_TITLE": "Spending Alert", "PARENT_SPENDING_MESSAGE": "{childName} spent {amount} {currency} at {merchant}. Remaining balance: {balance} {currency}", "YOUR_CHILD": "Your child",