mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2026-03-10 17:11:44 +00:00
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.
This commit is contained in:
@ -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);
|
||||
|
||||
@ -111,17 +111,53 @@ export class TransactionNotificationListener {
|
||||
const amount = transaction.transactionAmount;
|
||||
const merchant = transaction.merchantName || 'merchant';
|
||||
|
||||
// For child notifications, show the child's account balance (available balance)
|
||||
// For child notifications, show the appropriate balance based on account structure
|
||||
let balance = 0;
|
||||
let accountCurrency: string | undefined;
|
||||
|
||||
if (isTopUp) {
|
||||
// For top-up: show child's account balance after the transfer
|
||||
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 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 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 {
|
||||
// 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
|
||||
);
|
||||
@ -131,16 +167,12 @@ export class TransactionNotificationListener {
|
||||
`[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 fetch account by reference: ${error?.message}. Using card account balance.`
|
||||
`[Child Top-Up Notification] Could not fetch account: ${error?.message}. Using card balance.`
|
||||
);
|
||||
balance = card.account?.balance || 0;
|
||||
accountCurrency = card.account?.currency;
|
||||
|
||||
Reference in New Issue
Block a user