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}`);
// 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
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 (only if parent is funding)
card.parentId ? this.accountService.decreaseAccountBalance(fundingAccount.accountReference, amount) : Promise.resolve(),
]);
if (isSharedAccount) {
// Shared account: Only update card limit and reserved balance
// Money is already in the shared account, just allocate it to the child
this.logger.debug(`Shared account detected - only updating card limit and reserved balance`);
await Promise.all([
this.neoleapService.updateCardControl(card.cardReference, finalAmountNumber),
this.updateCardLimit(card.id, finalAmountNumber),
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
await this.transactionService.createInternalChildTransaction(card.id, amount);

View File

@ -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',

View File

@ -139,14 +139,39 @@ export class MoneyRequestNotificationListener {
const currency = getCurrency(accountCurrency, null, 'SAR');
const formattedAmount = formatCurrencyAmount(amount, currency);
const locale = this.getUserLocale(parentUser);
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({
userId: parentUser.id,
title: 'Money Request',
message: `${childName} requested ${formattedAmount} ${currency}. Reason: ${reason}`,
title,
message,
scope: NotificationScope.MONEY_REQUEST_CREATED,
preferences: this.getUserPreferences(parentUser),
data: {

View File

@ -1,6 +1,7 @@
import { Injectable, Logger } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { I18nService } from 'nestjs-i18n';
import { Roles } from '~/auth/enums';
import { NotificationFactory, NotificationPreferences } from '../services/notification-factory.service';
import { UserService } from '~/user/services/user.service';
import { NOTIFICATION_EVENTS } from '../constants/event-names.constant';
@ -24,6 +25,14 @@ export class ProfileNotificationListener {
try {
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(
`Processing profile updated notification for user ${user.id} - Updated fields: ${updatedFields.join(', ')}`
);

View File

@ -97,36 +97,86 @@ 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 appropriate balance based on account structure
let balance = 0;
let accountCurrency: string | undefined;
if (isTopUp) {
// For top-up: show card limit (newAmount from transfer response)
if (isTopUp && isChildSpending) {
// Internal transfer: For shared accounts, show card limit (child's spending power)
// For separate accounts, show child's account balance
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 updated data
const cardWithUpdatedBalance = await this.cardService.getCardById(card.id);
// Check if child has parent (shared account scenario)
if (cardWithUpdatedBalance.parentId) {
// 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) {
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;
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 {
// For spending: show account balance
try {
@ -176,36 +226,44 @@ 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,
balance: formattedBalance,
},
});
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);
// Fallback to English without i18n
title = isTopUp ? 'Card Topped Up' : 'Purchase Successful';
message = isTopUp
? `You received ${formattedAmount} ${currency}. Total balance: ${formattedBalance} ${currency}`
: `You spent ${formattedAmount} ${currency} at ${merchant}. Balance: ${formattedBalance} ${currency}`;
title = isTopUp ? 'Funds Credited' : 'Purchase Successful';
message = isTopUp
? `${formattedAmount} ${currency} has been added to your card. Total balance: ${formattedBalance} ${currency}`
: `You spent ${formattedAmount} ${currency} at ${merchant}`;
}
this.logger.debug(
@ -263,29 +321,80 @@ export class TransactionNotificationListener {
const amount = transaction.transactionAmount;
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 parentAccountReservedBalance = 0;
let parentAccountCurrency: string | undefined;
let availableBalance = 0;
try {
if (card.parentId) {
// Always reload parent account to get fresh balance after transaction
const parentAccount = await this.accountService.getAccountByCustomerId(card.parentId);
parentAccountBalance = parentAccount.balance;
parentAccountCurrency = parentAccount.currency;
this.logger.debug(`Fetched parent account balance: ${parentAccountBalance}, currency: ${parentAccountCurrency}`);
// 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;
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 {
const parentCustomer = customer?.junior?.guardian?.customer;
if (parentCustomer?.cards?.[0]?.account) {
// Reload to get fresh balance
const parentAccount = await this.accountService.getAccountByCustomerId(parentCustomer.id);
parentAccountBalance = parentAccount.balance;
parentAccountCurrency = parentAccount.currency;
this.logger.debug(`Fetched parent account balance via customer: ${parentAccountBalance}, currency: ${parentAccountCurrency}`);
if (parentCustomer?.id) {
try {
const parentCard = await this.cardService.getCardByCustomerId(parentCustomer.id);
if (parentCard?.account?.accountReference) {
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 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) {
this.logger.warn(`Could not fetch parent account for parent notification: ${error?.message}, using child account balance as fallback`);
parentAccountBalance = card.account?.balance || 0;
this.logger.warn(`[Parent Spending] Could not fetch parent account: ${error?.message}, using child account balance as fallback`);
availableBalance = card.account?.balance || 0;
parentAccountCurrency = card.account?.currency;
}
@ -297,11 +406,12 @@ export class TransactionNotificationListener {
);
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 formattedBalance = formatCurrencyAmount(parentAccountBalance, currency);
// Use available balance for parent spending notification
const formattedBalance = formatCurrencyAmount(availableBalance, currency);
this.logger.debug(
`Notifying parent (user ${parentUser.id}): ${childName} spent ${formattedAmount} ${currency} at ${merchant}`
@ -325,8 +435,8 @@ export class TransactionNotificationListener {
} catch (i18nError: any) {
console.error(`[TransactionNotificationListener] i18n error in parent spending:`, i18nError);
this.logger.error(`i18n translation failed: ${i18nError?.message}`, i18nError?.stack);
title = 'Child Spending Alert';
message = `${childName} spent ${formattedAmount} ${currency} at ${merchant}. Balance: ${formattedBalance} ${currency}`;
title = 'Spending Alert';
message = `${childName} spent ${formattedAmount} ${currency} at ${merchant}. Remaining balance: ${formattedBalance} ${currency}`;
}
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
*/
private async notifyParentOfTopUp(transaction: Transaction, card: Card): Promise<void> {
@ -479,15 +589,15 @@ export class TransactionNotificationListener {
const formattedBalance = formatCurrencyAmount(balance, currency);
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 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,
@ -497,17 +607,17 @@ 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 = 'Top-Up Confirmation';
message = `You transferred ${formattedAmount} ${currency} to ${childName}. Balance: ${formattedBalance} ${currency}`;
title = 'Internal Transfer Completed';
message = `${formattedAmount} ${currency} has been transferred to ${childName}'s card. ${childName}'s balance is ${formattedBalance} ${currency}`;
}
await this.notificationFactory.send({
userId: parentUser.id,
title,
message,
scope: NotificationScope.PARENT_TOP_UP_CONFIRMATION,
scope: NotificationScope.PARENT_INTERNAL_TRANSFER,
preferences: this.getUserPreferences(parentUser),
data: {
transactionId: transaction.id,

View File

@ -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}. الرصيد: {balance} {currency}",
"PARENT_TOP_UP_TITLE": "تأكيد الشحن",
"PARENT_TOP_UP_MESSAGE": "لقد قمت بتحويل {amount} {currency} إلى {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": "تم رفض طلب المال",

View File

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