Compare commits

...

4 Commits

Author SHA1 Message Date
2ab9554c0c feat: add profile update notification handling for child users
- Implemented logic to skip notifications for child users (roles.JUNIOR) when their profiles are updated, preventing unnecessary notifications to both child and parent.
- Enhanced logging to indicate when notifications are skipped for child users.
2026-02-01 14:44:42 +03:00
f5c3b03264 feat: enhance card service and transaction notification for shared account handling
- Updated CardService to differentiate between shared and separate accounts during card control updates, optimizing balance allocation.
- Enhanced TransactionNotificationListener to accurately reflect balance based on account structure for internal transfers and top-ups.
- Improved logging for better traceability of account operations and balance calculations.
2026-01-20 16:27:32 +03:00
a09b84e475 feat: enhance transaction notification listener for internal transfer support
- Updated TransactionNotificationListener to differentiate between internal transfers and external top-ups for child accounts.
- Added new notification scopes and messages for internal transfers from parent to child.
- Improved balance retrieval logic to ensure accurate account balances are displayed in notifications.
- Enhanced localization support by adding relevant keys for internal transfer notifications in both English and Arabic.
2026-01-20 15:20:58 +03:00
4305c4b75f fix the messages 2026-01-20 14:40:16 +03:00
7 changed files with 271 additions and 98 deletions

View File

@ -240,16 +240,37 @@ export class CardService {
this.logger.debug(`Updating card control - cardReference: ${card.cardReference}, finalAmount: ${finalAmountNumber}`); this.logger.debug(`Updating card control - cardReference: ${card.cardReference}, finalAmount: ${finalAmountNumber}`);
// Check if child and parent share the same account
const isSharedAccount = card.parentId && card.account.id === fundingAccount.id;
this.logger.debug(
`Account structure - Child account: ${card.account.id}, Parent account: ${fundingAccount.id}, ` +
`Shared: ${isSharedAccount ? 'YES' : 'NO'}`
);
// First, ensure all external operations succeed before creating transaction // First, ensure all external operations succeed before creating transaction
await Promise.all([ if (isSharedAccount) {
this.neoleapService.updateCardControl(card.cardReference, finalAmountNumber), // Shared account: Only update card limit and reserved balance
this.updateCardLimit(card.id, finalAmountNumber), // Money is already in the shared account, just allocate it to the child
this.accountService.increaseReservedBalance(fundingAccount, amount), this.logger.debug(`Shared account detected - only updating card limit and reserved balance`);
// Increase child account balance await Promise.all([
this.accountService.creditAccountBalance(card.account.accountReference, amount), this.neoleapService.updateCardControl(card.cardReference, finalAmountNumber),
// Decrease parent account balance (only if parent is funding) this.updateCardLimit(card.id, finalAmountNumber),
card.parentId ? this.accountService.decreaseAccountBalance(fundingAccount.accountReference, amount) : Promise.resolve(), this.accountService.increaseReservedBalance(fundingAccount, amount),
]); ]);
} else {
// Separate accounts: Transfer money from parent to child
this.logger.debug(`Separate accounts - transferring money from parent to child`);
await Promise.all([
this.neoleapService.updateCardControl(card.cardReference, finalAmountNumber),
this.updateCardLimit(card.id, finalAmountNumber),
this.accountService.increaseReservedBalance(fundingAccount, amount),
// Increase child account balance
this.accountService.creditAccountBalance(card.account.accountReference, amount),
// Decrease parent account balance
this.accountService.decreaseAccountBalance(fundingAccount.accountReference, amount),
]);
}
// Only create transaction and emit event after all operations succeed // Only create transaction and emit event after all operations succeed
await this.transactionService.createInternalChildTransaction(card.id, amount); await this.transactionService.createInternalChildTransaction(card.id, amount);

View File

@ -6,10 +6,14 @@ export enum NotificationScope {
OTP = 'OTP', OTP = 'OTP',
USER_INVITED = 'USER_INVITED', USER_INVITED = 'USER_INVITED',
// Transaction notifications - Top-up // Transaction notifications - Top-up (external funds)
CHILD_TOP_UP = 'CHILD_TOP_UP', CHILD_TOP_UP = 'CHILD_TOP_UP',
PARENT_TOP_UP_CONFIRMATION = 'PARENT_TOP_UP_CONFIRMATION', 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 // Transaction notifications - Spending
CHILD_SPENDING = 'CHILD_SPENDING', CHILD_SPENDING = 'CHILD_SPENDING',
PARENT_SPENDING_ALERT = 'PARENT_SPENDING_ALERT', PARENT_SPENDING_ALERT = 'PARENT_SPENDING_ALERT',

View File

@ -139,14 +139,39 @@ export class MoneyRequestNotificationListener {
const currency = getCurrency(accountCurrency, null, 'SAR'); const currency = getCurrency(accountCurrency, null, 'SAR');
const formattedAmount = formatCurrencyAmount(amount, currency); const formattedAmount = formatCurrencyAmount(amount, currency);
const locale = this.getUserLocale(parentUser);
this.logger.debug( this.logger.debug(
`Notifying parent (user ${parentUser.id}): ${childName} requested ${formattedAmount} ${currency} - ${reason}` `Notifying parent (user ${parentUser.id}): ${childName} requested ${formattedAmount} ${currency} for ${reason}`
); );
let title: string;
let message: string;
try {
title = this.i18n.t('app.NOTIFICATION.MONEY_REQUEST_CREATED_TITLE', { lang: locale });
message = this.i18n.t('app.NOTIFICATION.MONEY_REQUEST_CREATED_MESSAGE', {
lang: locale,
args: {
childName: childName,
amount: formattedAmount,
currency: currency,
reason: reason,
},
});
} catch (i18nError: any) {
this.logger.error(
`[MoneyRequestNotificationListener] i18n error for parent ${parentUser.id}: ${i18nError?.message || 'Unknown i18n error'}. Falling back to English.`,
i18nError?.stack
);
title = 'Money Request';
message = `${childName} has requested ${formattedAmount} ${currency} for ${reason}.`;
}
await this.notificationFactory.send({ await this.notificationFactory.send({
userId: parentUser.id, userId: parentUser.id,
title: 'Money Request', title,
message: `${childName} requested ${formattedAmount} ${currency}. Reason: ${reason}`, message,
scope: NotificationScope.MONEY_REQUEST_CREATED, scope: NotificationScope.MONEY_REQUEST_CREATED,
preferences: this.getUserPreferences(parentUser), preferences: this.getUserPreferences(parentUser),
data: { data: {

View File

@ -1,6 +1,7 @@
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter'; import { OnEvent } from '@nestjs/event-emitter';
import { I18nService } from 'nestjs-i18n'; import { I18nService } from 'nestjs-i18n';
import { Roles } from '~/auth/enums';
import { NotificationFactory, NotificationPreferences } from '../services/notification-factory.service'; import { NotificationFactory, NotificationPreferences } from '../services/notification-factory.service';
import { UserService } from '~/user/services/user.service'; import { UserService } from '~/user/services/user.service';
import { NOTIFICATION_EVENTS } from '../constants/event-names.constant'; import { NOTIFICATION_EVENTS } from '../constants/event-names.constant';
@ -24,6 +25,14 @@ export class ProfileNotificationListener {
try { try {
const { user, updatedFields } = event; const { user, updatedFields } = event;
// Do not notify when a child updates their profile (no notification to child or parent)
if (user?.roles?.includes(Roles.JUNIOR)) {
this.logger.log(
`Skipping profile updated notification for child user ${user.id} - no notification sent`
);
return;
}
this.logger.log( this.logger.log(
`Processing profile updated notification for user ${user.id} - Updated fields: ${updatedFields.join(', ')}` `Processing profile updated notification for user ${user.id} - Updated fields: ${updatedFields.join(', ')}`
); );

View File

@ -97,36 +97,86 @@ export class TransactionNotificationListener {
return; return;
} }
const scope = isTopUp // Determine scope: internal transfer (parent to child) vs external top-up
? NotificationScope.CHILD_TOP_UP let scope: NotificationScope;
: NotificationScope.CHILD_SPENDING; 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 locale = this.getUserLocale(user);
const amount = transaction.transactionAmount; const amount = transaction.transactionAmount;
const merchant = transaction.merchantName || 'merchant'; const merchant = transaction.merchantName || 'merchant';
// For child top-up notifications, show card.limit (newAmount) instead of account balance // For child notifications, show the appropriate balance based on account structure
// card.limit represents the total spending limit on the card after the transfer
let balance = 0; let balance = 0;
let accountCurrency: string | undefined; let accountCurrency: string | undefined;
if (isTopUp) { if (isTopUp && isChildSpending) {
// For top-up: show card limit (newAmount from transfer response) // Internal transfer: For shared accounts, show card limit (child's spending power)
// For separate accounts, show child's account balance
try { try {
// Reload card to get updated limit // Reload card to get updated data
const cardWithUpdatedLimit = await this.cardService.getCardById(card.id); const cardWithUpdatedBalance = await this.cardService.getCardById(card.id);
balance = cardWithUpdatedLimit.limit || card.limit || 0;
accountCurrency = cardWithUpdatedLimit.account?.currency || card.account?.currency; // Check if child has parent (shared account scenario)
this.logger.debug( if (cardWithUpdatedBalance.parentId) {
`[Child Top-Up Notification] Using card limit (newAmount) - limit: ${balance} ${accountCurrency}` // Likely shared account - use card limit as the child's "balance"
); balance = cardWithUpdatedBalance.limit || card.limit || 0;
accountCurrency = cardWithUpdatedBalance.account?.currency || card.account?.currency;
this.logger.debug(
`[Child Internal Transfer] Shared account - using card limit: ${balance} ${accountCurrency}`
);
} else {
// Separate account - use child's account balance
if (cardWithUpdatedBalance?.account?.accountReference) {
const account = await this.accountService.getAccountByReferenceNumber(
cardWithUpdatedBalance.account.accountReference
);
balance = account.balance;
accountCurrency = account.currency;
this.logger.debug(
`[Child Internal Transfer] Separate account - using account balance: ${balance} ${accountCurrency}`
);
} else {
balance = cardWithUpdatedBalance.account?.balance || card.account?.balance || 0;
accountCurrency = cardWithUpdatedBalance.account?.currency || card.account?.currency;
}
}
} catch (error: any) { } catch (error: any) {
this.logger.warn( this.logger.warn(
`[Child Top-Up Notification] Could not reload card: ${error?.message}. Using card limit from event.` `[Child Internal Transfer] Could not fetch balance: ${error?.message}. Using card limit.`
); );
balance = card.limit || 0; balance = card.limit || 0;
accountCurrency = card.account?.currency; accountCurrency = card.account?.currency;
} }
} else if (isTopUp) {
// External top-up: show child's account balance
try {
const cardWithUpdatedBalance = await this.cardService.getCardById(card.id);
if (cardWithUpdatedBalance?.account?.accountReference) {
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 {
balance = cardWithUpdatedBalance.account?.balance || card.account?.balance || 0;
accountCurrency = cardWithUpdatedBalance.account?.currency || card.account?.currency;
}
} catch (error: any) {
this.logger.warn(
`[Child Top-Up Notification] Could not fetch account: ${error?.message}. Using card balance.`
);
balance = card.account?.balance || 0;
accountCurrency = card.account?.currency;
}
} else { } else {
// For spending: show account balance // For spending: show account balance
try { try {
@ -176,36 +226,44 @@ export class TransactionNotificationListener {
let message: string; let message: string;
try { try {
title = isTopUp if (isTopUp) {
? this.i18n.t('app.NOTIFICATION.CHILD_TOP_UP_TITLE', { lang: locale }) // Internal transfer or external top-up
: this.i18n.t('app.NOTIFICATION.CHILD_SPENDING_TITLE', { lang: locale }); const titleKey = isChildSpending
? 'app.NOTIFICATION.CHILD_INTERNAL_TRANSFER_TITLE'
message = isTopUp : 'app.NOTIFICATION.CHILD_TOP_UP_TITLE';
? this.i18n.t('app.NOTIFICATION.CHILD_TOP_UP_MESSAGE', { const messageKey = isChildSpending
lang: locale, ? 'app.NOTIFICATION.CHILD_INTERNAL_TRANSFER_MESSAGE'
args: { : 'app.NOTIFICATION.CHILD_TOP_UP_MESSAGE';
amount: formattedAmount,
currency: currency, title = this.i18n.t(titleKey, { lang: locale });
balance: formattedBalance, message = this.i18n.t(messageKey, {
}, lang: locale,
}) args: {
: this.i18n.t('app.NOTIFICATION.CHILD_SPENDING_MESSAGE', { amount: formattedAmount,
lang: locale, currency: currency,
args: { balance: formattedBalance,
amount: formattedAmount, },
currency: currency, });
merchant: merchant, } else {
balance: formattedBalance, // 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) { } catch (i18nError: any) {
console.error(`[TransactionNotificationListener] i18n error:`, i18nError); console.error(`[TransactionNotificationListener] i18n error:`, i18nError);
this.logger.error(`i18n translation failed: ${i18nError?.message}`, i18nError?.stack); this.logger.error(`i18n translation failed: ${i18nError?.message}`, i18nError?.stack);
// Fallback to English without i18n // Fallback to English without i18n
title = isTopUp ? 'Card Topped Up' : 'Purchase Successful'; title = isTopUp ? 'Funds Credited' : 'Purchase Successful';
message = isTopUp message = isTopUp
? `You received ${formattedAmount} ${currency}. Total balance: ${formattedBalance} ${currency}` ? `${formattedAmount} ${currency} has been added to your card. Total balance: ${formattedBalance} ${currency}`
: `You spent ${formattedAmount} ${currency} at ${merchant}. Balance: ${formattedBalance} ${currency}`; : `You spent ${formattedAmount} ${currency} at ${merchant}`;
} }
this.logger.debug( this.logger.debug(
@ -263,29 +321,80 @@ export class TransactionNotificationListener {
const amount = transaction.transactionAmount; const amount = transaction.transactionAmount;
const merchant = transaction.merchantName || 'a merchant'; const merchant = transaction.merchantName || 'a merchant';
// Get parent's account balance (not child's balance) - reload to get fresh balance // Get parent's available balance (balance - reserved_balance) - reload to get fresh balance
let parentAccountBalance = 0; let parentAccountBalance = 0;
let parentAccountReservedBalance = 0;
let parentAccountCurrency: string | undefined; let parentAccountCurrency: string | undefined;
let availableBalance = 0;
try { try {
if (card.parentId) { if (card.parentId) {
// Always reload parent account to get fresh balance after transaction // Get parent's card to access their account reference
const parentAccount = await this.accountService.getAccountByCustomerId(card.parentId); const parentCard = await this.cardService.getCardByCustomerId(card.parentId);
parentAccountBalance = parentAccount.balance; if (parentCard?.account?.accountReference) {
parentAccountCurrency = parentAccount.currency; // Fetch by reference number to get fresh balance from database
this.logger.debug(`Fetched parent account balance: ${parentAccountBalance}, currency: ${parentAccountCurrency}`); const parentAccount = await this.accountService.getAccountByReferenceNumber(
parentCard.account.accountReference
);
parentAccountBalance = parentAccount.balance;
parentAccountReservedBalance = parentAccount.reservedBalance;
availableBalance = parentAccountBalance - parentAccountReservedBalance;
parentAccountCurrency = parentAccount.currency;
this.logger.debug(
`[Parent Spending] Fetched parent account by reference - balance: ${parentAccountBalance}, reserved: ${parentAccountReservedBalance}, available: ${availableBalance} ${parentAccountCurrency}`
);
} else {
// Fallback: try by customer ID
const parentAccount = await this.accountService.getAccountByCustomerId(card.parentId);
parentAccountBalance = parentAccount.balance;
parentAccountReservedBalance = parentAccount.reservedBalance;
availableBalance = parentAccountBalance - parentAccountReservedBalance;
parentAccountCurrency = parentAccount.currency;
this.logger.debug(
`[Parent Spending] Fetched parent account by customer ID - balance: ${parentAccountBalance}, reserved: ${parentAccountReservedBalance}, available: ${availableBalance} ${parentAccountCurrency}`
);
}
} else { } else {
const parentCustomer = customer?.junior?.guardian?.customer; const parentCustomer = customer?.junior?.guardian?.customer;
if (parentCustomer?.cards?.[0]?.account) { if (parentCustomer?.id) {
// Reload to get fresh balance try {
const parentAccount = await this.accountService.getAccountByCustomerId(parentCustomer.id); const parentCard = await this.cardService.getCardByCustomerId(parentCustomer.id);
parentAccountBalance = parentAccount.balance; if (parentCard?.account?.accountReference) {
parentAccountCurrency = parentAccount.currency; const parentAccount = await this.accountService.getAccountByReferenceNumber(
this.logger.debug(`Fetched parent account balance via customer: ${parentAccountBalance}, currency: ${parentAccountCurrency}`); parentCard.account.accountReference
);
parentAccountBalance = parentAccount.balance;
parentAccountReservedBalance = parentAccount.reservedBalance;
availableBalance = parentAccountBalance - parentAccountReservedBalance;
parentAccountCurrency = parentAccount.currency;
this.logger.debug(
`[Parent Spending] Fetched parent account via customer relation (by reference) - balance: ${parentAccountBalance}, reserved: ${parentAccountReservedBalance}, available: ${availableBalance} ${parentAccountCurrency}`
);
} else {
const parentAccount = await this.accountService.getAccountByCustomerId(parentCustomer.id);
parentAccountBalance = parentAccount.balance;
parentAccountReservedBalance = parentAccount.reservedBalance;
availableBalance = parentAccountBalance - parentAccountReservedBalance;
parentAccountCurrency = parentAccount.currency;
this.logger.debug(
`[Parent Spending] Fetched parent account via customer relation - balance: ${parentAccountBalance}, reserved: ${parentAccountReservedBalance}, available: ${availableBalance} ${parentAccountCurrency}`
);
}
} catch (error: any) {
this.logger.warn(
`[Parent Spending] Could not fetch parent account via customer: ${error?.message}. Using child account balance as fallback.`
);
availableBalance = card.account?.balance || 0;
parentAccountCurrency = card.account?.currency;
}
} else {
availableBalance = card.account?.balance || 0;
parentAccountCurrency = card.account?.currency;
} }
} }
} catch (error: any) { } catch (error: any) {
this.logger.warn(`Could not fetch parent account for parent notification: ${error?.message}, using child account balance as fallback`); this.logger.warn(`[Parent Spending] Could not fetch parent account: ${error?.message}, using child account balance as fallback`);
parentAccountBalance = card.account?.balance || 0; availableBalance = card.account?.balance || 0;
parentAccountCurrency = card.account?.currency; parentAccountCurrency = card.account?.currency;
} }
@ -297,11 +406,12 @@ export class TransactionNotificationListener {
); );
this.logger.debug( this.logger.debug(
`[Parent Spending Notification] Parent account currency: ${parentAccountCurrency}, Account currency: ${accountCurrency}, Transaction currency: ${transaction.transactionCurrency}, Final currency: ${currency}, Parent balance: ${parentAccountBalance}, Amount: ${amount}` `[Parent Spending Notification] Parent account currency: ${parentAccountCurrency}, Account currency: ${accountCurrency}, Transaction currency: ${transaction.transactionCurrency}, Final currency: ${currency}, Parent available balance: ${availableBalance}, Amount: ${amount}`
); );
const formattedAmount = formatCurrencyAmount(amount, currency); const formattedAmount = formatCurrencyAmount(amount, currency);
const formattedBalance = formatCurrencyAmount(parentAccountBalance, currency); // Use available balance for parent spending notification
const formattedBalance = formatCurrencyAmount(availableBalance, currency);
this.logger.debug( this.logger.debug(
`Notifying parent (user ${parentUser.id}): ${childName} spent ${formattedAmount} ${currency} at ${merchant}` `Notifying parent (user ${parentUser.id}): ${childName} spent ${formattedAmount} ${currency} at ${merchant}`
@ -325,8 +435,8 @@ export class TransactionNotificationListener {
} catch (i18nError: any) { } catch (i18nError: any) {
console.error(`[TransactionNotificationListener] i18n error in parent spending:`, i18nError); console.error(`[TransactionNotificationListener] i18n error in parent spending:`, i18nError);
this.logger.error(`i18n translation failed: ${i18nError?.message}`, i18nError?.stack); this.logger.error(`i18n translation failed: ${i18nError?.message}`, i18nError?.stack);
title = 'Child Spending Alert'; title = 'Spending Alert';
message = `${childName} spent ${formattedAmount} ${currency} at ${merchant}. Balance: ${formattedBalance} ${currency}`; message = `${childName} spent ${formattedAmount} ${currency} at ${merchant}. Remaining balance: ${formattedBalance} ${currency}`;
} }
await this.notificationFactory.send({ await this.notificationFactory.send({
@ -360,7 +470,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 * This is a confirmation notification for the parent
*/ */
private async notifyParentOfTopUp(transaction: Transaction, card: Card): Promise<void> { private async notifyParentOfTopUp(transaction: Transaction, card: Card): Promise<void> {
@ -479,15 +589,15 @@ export class TransactionNotificationListener {
const formattedBalance = formatCurrencyAmount(balance, currency); const formattedBalance = formatCurrencyAmount(balance, currency);
this.logger.debug( this.logger.debug(
`Notifying parent (user ${parentUser.id}): Transferred ${formattedAmount} ${currency} to ${childName}, parent balance: ${formattedBalance} ${currency}` `Notifying parent (user ${parentUser.id}): Transferred ${formattedAmount} ${currency} to ${childName}, child balance: ${formattedBalance} ${currency}`
); );
let title: string; let title: string;
let message: string; let message: string;
try { try {
title = this.i18n.t('app.NOTIFICATION.PARENT_TOP_UP_TITLE', { lang: locale }); title = this.i18n.t('app.NOTIFICATION.PARENT_INTERNAL_TRANSFER_TITLE', { lang: locale });
message = this.i18n.t('app.NOTIFICATION.PARENT_TOP_UP_MESSAGE', { message = this.i18n.t('app.NOTIFICATION.PARENT_INTERNAL_TRANSFER_MESSAGE', {
lang: locale, lang: locale,
args: { args: {
amount: formattedAmount, amount: formattedAmount,
@ -497,17 +607,17 @@ export class TransactionNotificationListener {
}, },
}); });
} catch (i18nError: any) { } 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); this.logger.error(`i18n translation failed: ${i18nError?.message}`, i18nError?.stack);
title = 'Top-Up Confirmation'; title = 'Internal Transfer Completed';
message = `You transferred ${formattedAmount} ${currency} to ${childName}. Balance: ${formattedBalance} ${currency}`; message = `${formattedAmount} ${currency} has been transferred to ${childName}'s card. ${childName}'s balance is ${formattedBalance} ${currency}`;
} }
await this.notificationFactory.send({ await this.notificationFactory.send({
userId: parentUser.id, userId: parentUser.id,
title, title,
message, message,
scope: NotificationScope.PARENT_TOP_UP_CONFIRMATION, scope: NotificationScope.PARENT_INTERNAL_TRANSFER,
preferences: this.getUserPreferences(parentUser), preferences: this.getUserPreferences(parentUser),
data: { data: {
transactionId: transaction.id, transactionId: transaction.id,

View File

@ -112,17 +112,19 @@
"NOT_FOUND": "لم يتم العثور على البطاقة." "NOT_FOUND": "لم يتم العثور على البطاقة."
}, },
"NOTIFICATION": { "NOTIFICATION": {
"CHILD_TOP_UP_TITLE": "تم شحن البطاقة", "CHILD_TOP_UP_TITLE": "تم إضافة رصيد",
"CHILD_TOP_UP_MESSAGE": "لقد استلمت {amount} {currency}. الرصيد الإجمالي: {balance} {currency}", "CHILD_TOP_UP_MESSAGE": "تمت إضافة {amount} {currency} إلى بطاقتك. إجمالي الرصيد: {balance} {currency}",
"CHILD_SPENDING_TITLE": "تمت العملية بنجاح", "CHILD_INTERNAL_TRANSFER_TITLE": "تم إضافة رصيد",
"CHILD_SPENDING_MESSAGE": "لقد أنفقت {amount} {currency} في {merchant}. الرصيد: {balance} {currency}", "CHILD_INTERNAL_TRANSFER_MESSAGE": "تمت إضافة {amount} {currency} إلى بطاقتك. إجمالي الرصيد: {balance} {currency}",
"PARENT_TOP_UP_TITLE": "تأكيد الشحن", "PARENT_INTERNAL_TRANSFER_TITLE": "اكتمل التحويل",
"PARENT_TOP_UP_MESSAGE": "لقد قمت بتحويل {amount} {currency} إلى {childName}. الرصيد: {balance} {currency}", "PARENT_INTERNAL_TRANSFER_MESSAGE": "تم تحويل {amount} {currency} إلى بطاقة {childName}. رصيد {childName}: {balance} {currency}",
"PARENT_SPENDING_TITLE": "تنبيه إنفاق الطفل", "CHILD_SPENDING_TITLE": "عملية شراء ناجحة",
"PARENT_SPENDING_MESSAGE": "أنفق {childName} {amount} {currency} في {merchant}. الرصيد: {balance} {currency}", "CHILD_SPENDING_MESSAGE": "قمت بإنفاق {amount} {currency} في {merchant}",
"PARENT_SPENDING_TITLE": "تنبيه صرف",
"PARENT_SPENDING_MESSAGE": "قام {childName} بإنفاق {amount} {currency} في {merchant}. الرصيد المتبقي: {balance} {currency}",
"YOUR_CHILD": "طفلك", "YOUR_CHILD": "طفلك",
"MONEY_REQUEST_CREATED_TITLE": "طلب مال", "MONEY_REQUEST_CREATED_TITLE": "طلب مبلغ مالي",
"MONEY_REQUEST_CREATED_MESSAGE": "طلب {childName} مبلغ {amount} {currency}. السبب: {reason}", "MONEY_REQUEST_CREATED_MESSAGE": "طلب {childName} مبلغ {amount} {currency} لـ {reason}",
"MONEY_REQUEST_APPROVED_TITLE": "تمت الموافقة على طلب المال", "MONEY_REQUEST_APPROVED_TITLE": "تمت الموافقة على طلب المال",
"MONEY_REQUEST_APPROVED_MESSAGE": "تمت الموافقة على طلبك بمبلغ {amount} {currency}. تمت إضافة المال إلى حسابك.", "MONEY_REQUEST_APPROVED_MESSAGE": "تمت الموافقة على طلبك بمبلغ {amount} {currency}. تمت إضافة المال إلى حسابك.",
"MONEY_REQUEST_DECLINED_TITLE": "تم رفض طلب المال", "MONEY_REQUEST_DECLINED_TITLE": "تم رفض طلب المال",

View File

@ -111,17 +111,19 @@
"NOT_FOUND": "The card was not found." "NOT_FOUND": "The card was not found."
}, },
"NOTIFICATION": { "NOTIFICATION": {
"CHILD_TOP_UP_TITLE": "Card Topped Up", "CHILD_TOP_UP_TITLE": "Funds Credited",
"CHILD_TOP_UP_MESSAGE": "You received {amount} {currency}. Total balance: {balance} {currency}", "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_TITLE": "Purchase Successful",
"CHILD_SPENDING_MESSAGE": "You spent {amount} {currency} at {merchant}. Balance: {balance} {currency}", "CHILD_SPENDING_MESSAGE": "You spent {amount} {currency} at {merchant}",
"PARENT_TOP_UP_TITLE": "Top-Up Confirmation", "PARENT_SPENDING_TITLE": "Spending Alert",
"PARENT_TOP_UP_MESSAGE": "You transferred {amount} {currency} to {childName}. Balance: {balance} {currency}", "PARENT_SPENDING_MESSAGE": "{childName} spent {amount} {currency} at {merchant}. Remaining balance: {balance} {currency}",
"PARENT_SPENDING_TITLE": "Child Spending Alert",
"PARENT_SPENDING_MESSAGE": "{childName} spent {amount} {currency} at {merchant}. Balance: {balance} {currency}",
"YOUR_CHILD": "Your child", "YOUR_CHILD": "Your child",
"MONEY_REQUEST_CREATED_TITLE": "Money Request", "MONEY_REQUEST_CREATED_TITLE": "Money Request",
"MONEY_REQUEST_CREATED_MESSAGE": "{childName} requested {amount} {currency}. Reason: {reason}", "MONEY_REQUEST_CREATED_MESSAGE": "{childName} has requested {amount} {currency} for {reason}.",
"MONEY_REQUEST_APPROVED_TITLE": "Money Request Approved", "MONEY_REQUEST_APPROVED_TITLE": "Money Request Approved",
"MONEY_REQUEST_APPROVED_MESSAGE": "Your request for {amount} {currency} has been approved. The money has been added to your account.", "MONEY_REQUEST_APPROVED_MESSAGE": "Your request for {amount} {currency} has been approved. The money has been added to your account.",
"MONEY_REQUEST_DECLINED_TITLE": "Money Request Declined", "MONEY_REQUEST_DECLINED_TITLE": "Money Request Declined",