create wallet transaction after order created

This commit is contained in:
2025-12-29 10:43:42 +03:30
parent 89adbfdb33
commit b9ef611a76
5 changed files with 107 additions and 70 deletions
+1
View File
@@ -10,4 +10,5 @@ export enum WalletTransactionReason {
CONVERT_SCORE_TO_WALLET = 'convert_score_to_wallet',
WITHDRAW = 'withdraw',
DEPOSIT = 'deposit',
ORDER_COMPLETED_DEPOSIT = 'order_completed_deposit',
}
+55 -43
View File
@@ -2,6 +2,7 @@ import { Injectable, NotFoundException, BadRequestException } from '@nestjs/comm
import { FilterQuery, RequiredEntityData } from '@mikro-orm/core';
import { User } from '../entities/user.entity';
import { UserAddress } from '../entities/user-address.entity';
import { Restaurant } from 'src/modules/restaurants/entities/restaurant.entity';
import { EntityManager } from '@mikro-orm/postgresql';
import { UpdateUserDto } from '../dto/update-user.dto';
import { FindUsersDto } from '../dto/find-user.dto';
@@ -16,6 +17,7 @@ import { RestRepository } from 'src/modules/restaurants/repositories/rest.reposi
import { UserWallet } from '../entities/user-wallet.entity';
import { UserWalletRepository } from '../repositories/user-wallet.repository';
import { WalletService } from './wallet.service';
import { WalletTransactionReason, WalletTransactionType } from '../interface/wallet';
@Injectable()
export class UserService {
@@ -298,54 +300,62 @@ export class UserService {
}
async convertScoreToWallet(userId: string, restId: string) {
const user = await this.userRepository.findOne({ id: userId });
if (!user) {
throw new NotFoundException(`User with ID ${userId} not found.`);
}
const restaurant = await this.restaurantRepository.findOne({ id: restId });
if (!restaurant) {
throw new NotFoundException(`Restaurant with ID ${restId} not found.`);
}
let userWallet = await this.userWalletRepository.findOne({ user: { id: userId }, restaurant: { id: restId } });
if (!userWallet) {
userWallet = this.userWalletRepository.create({ user: user, restaurant, wallet: 0, points: 0 });
await this.em.persistAndFlush(userWallet);
}
// Determine how many points to convert
const pointsToConvert = userWallet.points;
return this.em.transactional(async em => {
const user = await em.findOne(User, { id: userId });
if (!user) {
throw new NotFoundException(`User with ID ${userId} not found.`);
}
const restaurant = await em.findOne(Restaurant, { id: restId });
if (!restaurant) {
throw new NotFoundException(`Restaurant with ID ${restId} not found.`);
}
let userWallet = await em.findOne(UserWallet, { user: { id: userId }, restaurant: { id: restId } });
if (!userWallet) {
userWallet = em.create(UserWallet, { user: user, restaurant, wallet: 0, points: 0 });
await em.persistAndFlush(userWallet);
}
// Determine how many points to convert
const pointsToConvert = userWallet.points;
// Validate points to convert
if (pointsToConvert <= 0) {
throw new BadRequestException('Points to convert must be greater than 0.');
}
// Validate points to convert
if (pointsToConvert <= 0) {
throw new BadRequestException('Points to convert must be greater than 0.');
}
if (userWallet.points < pointsToConvert) {
throw new BadRequestException(
`Insufficient points. Available: ${userWallet.points}, Requested: ${pointsToConvert}.`,
);
}
if (userWallet.points < pointsToConvert) {
throw new BadRequestException(
`Insufficient points. Available: ${userWallet.points}, Requested: ${pointsToConvert}.`,
);
}
if (!restaurant.score) {
throw new BadRequestException('Restaurant score not found.');
}
if (!restaurant.score.purchaseScore) {
throw new BadRequestException('Restaurant purchase score not found.');
}
if (!restaurant.score.purchaseAmount) {
throw new BadRequestException('Restaurant purchase amount not found.');
}
const walletIncreaseAmount =
(Number(pointsToConvert) * Number(restaurant.score.purchaseAmount)) / Number(restaurant.score.purchaseScore);
// Convert points to wallet (1:1 ratio - can be made configurable)
if (!restaurant.score) {
throw new BadRequestException('Restaurant score not found.');
}
if (!restaurant.score.purchaseScore) {
throw new BadRequestException('Restaurant purchase score not found.');
}
if (!restaurant.score.purchaseAmount) {
throw new BadRequestException('Restaurant purchase amount not found.');
}
const walletIncreaseAmount =
(Number(pointsToConvert) * Number(restaurant.score.purchaseAmount)) / Number(restaurant.score.purchaseScore);
// Convert points to wallet (1:1 ratio - can be made configurable)
// Update user's wallet and points
userWallet.wallet = (userWallet.wallet || 0) + walletIncreaseAmount;
userWallet.points = (userWallet.points || 0) - pointsToConvert;
this.walletService.createTransaction(em, userId, restId, {
amount: walletIncreaseAmount,
type: WalletTransactionType.CREDIT,
reason: WalletTransactionReason.CONVERT_SCORE_TO_WALLET,
});
// Update user's wallet and points
userWallet.wallet = (userWallet.wallet || 0) + walletIncreaseAmount;
userWallet.points = (userWallet.points || 0) - pointsToConvert;
await this.em.flush();
await em.flush();
return user;
return user;
});
}
getUserWallet(userId: string, restId: string): Promise<UserWallet | null> {
return this.walletService.getUserWallet(userId, restId);
}
@@ -354,8 +364,10 @@ export class UserService {
return this.walletService.getUserWalletTransactions(userId, restId, dto);
}
createWalletTransaction(userId: string, restId: string, dto: CreateWalletTransactionDto) {
return this.walletService.createTransaction(userId, restId, dto);
async createWalletTransaction(userId: string, restId: string, dto: CreateWalletTransactionDto) {
return this.em.transactional(async em => {
return this.walletService.createTransaction(em, userId, restId, dto);
});
}
}
+16 -18
View File
@@ -113,6 +113,7 @@ export class WalletService {
}
async createTransaction(
em: EntityManager,
userId: string,
restId: string,
dto: CreateWalletTransactionDto,
@@ -125,7 +126,7 @@ export class WalletService {
}
// Find the user's wallet for this restaurant
const userWallet = await this.userWalletRepository.findOne({
const userWallet = await em.findOne(UserWallet, {
user: { id: userId },
restaurant: { id: restId },
});
@@ -151,24 +152,21 @@ export class WalletService {
throw new BadRequestException(`Invalid transaction type: ${type}`);
}
// Use transaction to ensure consistency
return await this.em.transactional(async (em) => {
// Update wallet balance
userWallet.wallet = newBalance;
await em.persistAndFlush(userWallet);
// Update wallet balance
userWallet.wallet = newBalance;
await em.persistAndFlush(userWallet);
// Create transaction record
const transaction = em.create(WalletTransaction, {
userWallet,
amount,
balance: newBalance,
type,
reason,
});
await em.persistAndFlush(transaction);
return transaction;
// Create transaction record
const transaction = em.create(WalletTransaction, {
userWallet,
amount,
balance: newBalance,
type,
reason,
});
await em.persistAndFlush(transaction);
return transaction;
}
}