create wallet transaction after order created
This commit is contained in:
@@ -9,22 +9,25 @@ import { ConfigService } from '@nestjs/config';
|
|||||||
import { OrderRepository } from '../repositories/order.repository';
|
import { OrderRepository } from '../repositories/order.repository';
|
||||||
import { OrderStatus } from '../interface/order.interface';
|
import { OrderStatus } from '../interface/order.interface';
|
||||||
import { PaymentMethodEnum } from 'src/modules/payments/interface/payment';
|
import { PaymentMethodEnum } from 'src/modules/payments/interface/payment';
|
||||||
|
import { UserService } from 'src/modules/users/providers/user.service';
|
||||||
|
import { WalletTransactionReason, WalletTransactionType } from 'src/modules/users/interface/wallet';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OrderListeners {
|
export class OrderListeners {
|
||||||
private readonly logger = new Logger(OrderListeners.name);
|
private readonly logger = new Logger(OrderListeners.name);
|
||||||
private orderCreatedSmsTemplateId: string;
|
private orderCreatedSmsTemplateId: string;
|
||||||
private orderStatusChangedSmsTemplateId: string;
|
private orderStatusChangedSmsTemplateId: string;
|
||||||
private orderCompletedSmsTemplateId: string;
|
// private orderCompletedSmsTemplateId: string;
|
||||||
constructor(
|
constructor(
|
||||||
private readonly adminService: AdminRepository,
|
private readonly adminService: AdminRepository,
|
||||||
private readonly OrderRepository: OrderRepository,
|
private readonly OrderRepository: OrderRepository,
|
||||||
private readonly notificationService: NotificationService,
|
private readonly notificationService: NotificationService,
|
||||||
private readonly configService: ConfigService,
|
private readonly configService: ConfigService,
|
||||||
|
private readonly userService: UserService,
|
||||||
) {
|
) {
|
||||||
this.orderCreatedSmsTemplateId = this.configService.get<string>('SMS_PATTERN_ORDER_CREATED') ?? '123';
|
this.orderCreatedSmsTemplateId = this.configService.get<string>('SMS_PATTERN_ORDER_CREATED') ?? '123';
|
||||||
this.orderStatusChangedSmsTemplateId = this.configService.get<string>('SMS_PATTERN_ORDER_STATUS_CHANGE') ?? '123';
|
this.orderStatusChangedSmsTemplateId = this.configService.get<string>('SMS_PATTERN_ORDER_STATUS_CHANGE') ?? '123';
|
||||||
this.orderCompletedSmsTemplateId = this.configService.get<string>('SMS_PATTERN_ORDER_STATUS_COMPLETED') ?? '123';
|
// this.orderCompletedSmsTemplateId = this.configService.get<string>('SMS_PATTERN_ORDER_STATUS_COMPLETED') ?? '123';
|
||||||
}
|
}
|
||||||
|
|
||||||
private getStatusFarsi(status: OrderStatus): string {
|
private getStatusFarsi(status: OrderStatus): string {
|
||||||
@@ -52,6 +55,8 @@ export class OrderListeners {
|
|||||||
if (order?.paymentMethod.method === PaymentMethodEnum.Online) {
|
if (order?.paymentMethod.method === PaymentMethodEnum.Online) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// get admnin os restuaraant that have order permissuins
|
// get admnin os restuaraant that have order permissuins
|
||||||
const admins = await this.adminService.findAdminsWithPermission(event.restaurantId, Permission.MANAGE_ORDERS);
|
const admins = await this.adminService.findAdminsWithPermission(event.restaurantId, Permission.MANAGE_ORDERS);
|
||||||
const recipients = admins.map(admin => ({
|
const recipients = admins.map(admin => ({
|
||||||
@@ -99,20 +104,39 @@ export class OrderListeners {
|
|||||||
this.logger.log(
|
this.logger.log(
|
||||||
`Order status changed event received: ${event.orderId} for restaurant: ${event.restaurantId} and order number: ${event.orderNumber}`,
|
`Order status changed event received: ${event.orderId} for restaurant: ${event.restaurantId} and order number: ${event.orderNumber}`,
|
||||||
);
|
);
|
||||||
|
//TODO : REFACTOR to use queue or other way to handle this
|
||||||
const recipients = [
|
const recipients = [
|
||||||
{
|
{
|
||||||
userId: event.userId,
|
userId: event.userId,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
if(event.newStatus === OrderStatus.COMPLETED) {
|
if (event.newStatus === OrderStatus.COMPLETED) {
|
||||||
|
|
||||||
|
if (!event?.userId) {
|
||||||
|
this.logger.log(
|
||||||
|
`User not found for order: ${event.orderId} for restaurant: ${event.restaurantId} and order number: ${event.orderNumber}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const order = await this.OrderRepository.findOne(event.orderId);
|
||||||
|
if (!order) {
|
||||||
|
this.logger.log(
|
||||||
|
`Order not found for order: ${event.orderId} for restaurant: ${event.restaurantId} and order number: ${event.orderNumber}`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.userService.createWalletTransaction(event.userId, event.restaurantId, {
|
||||||
|
amount: order.subTotal,
|
||||||
|
type: WalletTransactionType.CREDIT,
|
||||||
|
reason: WalletTransactionReason.ORDER_COMPLETED_DEPOSIT,
|
||||||
|
});
|
||||||
|
|
||||||
await this.notificationService.sendNotification({
|
await this.notificationService.sendNotification({
|
||||||
restaurantId: event.restaurantId,
|
restaurantId: event.restaurantId,
|
||||||
message: {
|
message: {
|
||||||
title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
content: `لطفابرای ثبت نظر سفارش ${event.orderNumber} به اپ مراجعه کنید`,
|
content: `لطفابرای ثبت نظر سفارش ${event.orderNumber} به اپ مراجعه کنید`,
|
||||||
sms: {
|
sms: {
|
||||||
templateId: this.orderCreatedSmsTemplateId ,
|
templateId: this.orderCreatedSmsTemplateId,
|
||||||
parameters: {
|
parameters: {
|
||||||
orderNumber: event.orderNumber,
|
orderNumber: event.orderNumber,
|
||||||
},
|
},
|
||||||
@@ -132,7 +156,7 @@ export class OrderListeners {
|
|||||||
priority: 1,
|
priority: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}else{
|
} else {
|
||||||
await this.notificationService.sendNotification({
|
await this.notificationService.sendNotification({
|
||||||
restaurantId: event.restaurantId,
|
restaurantId: event.restaurantId,
|
||||||
message: {
|
message: {
|
||||||
@@ -169,4 +193,4 @@ export class OrderListeners {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { OrdersCrone } from './crone/order.crone';
|
|||||||
import { AdminModule } from '../admin/admin.module';
|
import { AdminModule } from '../admin/admin.module';
|
||||||
import { NotificationsModule } from '../notifications/notifications.module';
|
import { NotificationsModule } from '../notifications/notifications.module';
|
||||||
import { InventoryModule } from '../inventory/inventory.module';
|
import { InventoryModule } from '../inventory/inventory.module';
|
||||||
|
import { UserModule } from '../users/user.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -31,7 +32,8 @@ import { InventoryModule } from '../inventory/inventory.module';
|
|||||||
JwtModule,
|
JwtModule,
|
||||||
AdminModule,
|
AdminModule,
|
||||||
NotificationsModule,
|
NotificationsModule,
|
||||||
InventoryModule
|
InventoryModule,
|
||||||
|
forwardRef(() => UserModule)
|
||||||
],
|
],
|
||||||
controllers: [OrdersController],
|
controllers: [OrdersController],
|
||||||
providers: [OrdersService, OrderRepository, OrderListeners, OrdersCrone],
|
providers: [OrdersService, OrderRepository, OrderListeners, OrdersCrone],
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ export enum WalletTransactionReason {
|
|||||||
CONVERT_SCORE_TO_WALLET = 'convert_score_to_wallet',
|
CONVERT_SCORE_TO_WALLET = 'convert_score_to_wallet',
|
||||||
WITHDRAW = 'withdraw',
|
WITHDRAW = 'withdraw',
|
||||||
DEPOSIT = 'deposit',
|
DEPOSIT = 'deposit',
|
||||||
|
ORDER_COMPLETED_DEPOSIT = 'order_completed_deposit',
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ import { Injectable, NotFoundException, BadRequestException } from '@nestjs/comm
|
|||||||
import { FilterQuery, RequiredEntityData } from '@mikro-orm/core';
|
import { FilterQuery, RequiredEntityData } from '@mikro-orm/core';
|
||||||
import { User } from '../entities/user.entity';
|
import { User } from '../entities/user.entity';
|
||||||
import { UserAddress } from '../entities/user-address.entity';
|
import { UserAddress } from '../entities/user-address.entity';
|
||||||
|
import { Restaurant } from 'src/modules/restaurants/entities/restaurant.entity';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { UpdateUserDto } from '../dto/update-user.dto';
|
import { UpdateUserDto } from '../dto/update-user.dto';
|
||||||
import { FindUsersDto } from '../dto/find-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 { UserWallet } from '../entities/user-wallet.entity';
|
||||||
import { UserWalletRepository } from '../repositories/user-wallet.repository';
|
import { UserWalletRepository } from '../repositories/user-wallet.repository';
|
||||||
import { WalletService } from './wallet.service';
|
import { WalletService } from './wallet.service';
|
||||||
|
import { WalletTransactionReason, WalletTransactionType } from '../interface/wallet';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
@@ -298,18 +300,19 @@ export class UserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async convertScoreToWallet(userId: string, restId: string) {
|
async convertScoreToWallet(userId: string, restId: string) {
|
||||||
const user = await this.userRepository.findOne({ id: userId });
|
return this.em.transactional(async em => {
|
||||||
|
const user = await em.findOne(User, { id: userId });
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new NotFoundException(`User with ID ${userId} not found.`);
|
throw new NotFoundException(`User with ID ${userId} not found.`);
|
||||||
}
|
}
|
||||||
const restaurant = await this.restaurantRepository.findOne({ id: restId });
|
const restaurant = await em.findOne(Restaurant, { id: restId });
|
||||||
if (!restaurant) {
|
if (!restaurant) {
|
||||||
throw new NotFoundException(`Restaurant with ID ${restId} not found.`);
|
throw new NotFoundException(`Restaurant with ID ${restId} not found.`);
|
||||||
}
|
}
|
||||||
let userWallet = await this.userWalletRepository.findOne({ user: { id: userId }, restaurant: { id: restId } });
|
let userWallet = await em.findOne(UserWallet, { user: { id: userId }, restaurant: { id: restId } });
|
||||||
if (!userWallet) {
|
if (!userWallet) {
|
||||||
userWallet = this.userWalletRepository.create({ user: user, restaurant, wallet: 0, points: 0 });
|
userWallet = em.create(UserWallet, { user: user, restaurant, wallet: 0, points: 0 });
|
||||||
await this.em.persistAndFlush(userWallet);
|
await em.persistAndFlush(userWallet);
|
||||||
}
|
}
|
||||||
// Determine how many points to convert
|
// Determine how many points to convert
|
||||||
const pointsToConvert = userWallet.points;
|
const pointsToConvert = userWallet.points;
|
||||||
@@ -338,14 +341,21 @@ export class UserService {
|
|||||||
(Number(pointsToConvert) * Number(restaurant.score.purchaseAmount)) / Number(restaurant.score.purchaseScore);
|
(Number(pointsToConvert) * Number(restaurant.score.purchaseAmount)) / Number(restaurant.score.purchaseScore);
|
||||||
// Convert points to wallet (1:1 ratio - can be made configurable)
|
// Convert points to wallet (1:1 ratio - can be made configurable)
|
||||||
|
|
||||||
|
this.walletService.createTransaction(em, userId, restId, {
|
||||||
|
amount: walletIncreaseAmount,
|
||||||
|
type: WalletTransactionType.CREDIT,
|
||||||
|
reason: WalletTransactionReason.CONVERT_SCORE_TO_WALLET,
|
||||||
|
});
|
||||||
// Update user's wallet and points
|
// Update user's wallet and points
|
||||||
userWallet.wallet = (userWallet.wallet || 0) + walletIncreaseAmount;
|
userWallet.wallet = (userWallet.wallet || 0) + walletIncreaseAmount;
|
||||||
userWallet.points = (userWallet.points || 0) - pointsToConvert;
|
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> {
|
getUserWallet(userId: string, restId: string): Promise<UserWallet | null> {
|
||||||
return this.walletService.getUserWallet(userId, restId);
|
return this.walletService.getUserWallet(userId, restId);
|
||||||
}
|
}
|
||||||
@@ -354,8 +364,10 @@ export class UserService {
|
|||||||
return this.walletService.getUserWalletTransactions(userId, restId, dto);
|
return this.walletService.getUserWalletTransactions(userId, restId, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
createWalletTransaction(userId: string, restId: string, dto: CreateWalletTransactionDto) {
|
async createWalletTransaction(userId: string, restId: string, dto: CreateWalletTransactionDto) {
|
||||||
return this.walletService.createTransaction(userId, restId, dto);
|
return this.em.transactional(async em => {
|
||||||
|
return this.walletService.createTransaction(em, userId, restId, dto);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ export class WalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createTransaction(
|
async createTransaction(
|
||||||
|
em: EntityManager,
|
||||||
userId: string,
|
userId: string,
|
||||||
restId: string,
|
restId: string,
|
||||||
dto: CreateWalletTransactionDto,
|
dto: CreateWalletTransactionDto,
|
||||||
@@ -125,7 +126,7 @@ export class WalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find the user's wallet for this restaurant
|
// Find the user's wallet for this restaurant
|
||||||
const userWallet = await this.userWalletRepository.findOne({
|
const userWallet = await em.findOne(UserWallet, {
|
||||||
user: { id: userId },
|
user: { id: userId },
|
||||||
restaurant: { id: restId },
|
restaurant: { id: restId },
|
||||||
});
|
});
|
||||||
@@ -151,8 +152,6 @@ export class WalletService {
|
|||||||
throw new BadRequestException(`Invalid transaction type: ${type}`);
|
throw new BadRequestException(`Invalid transaction type: ${type}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use transaction to ensure consistency
|
|
||||||
return await this.em.transactional(async (em) => {
|
|
||||||
// Update wallet balance
|
// Update wallet balance
|
||||||
userWallet.wallet = newBalance;
|
userWallet.wallet = newBalance;
|
||||||
await em.persistAndFlush(userWallet);
|
await em.persistAndFlush(userWallet);
|
||||||
@@ -169,6 +168,5 @@ export class WalletService {
|
|||||||
await em.persistAndFlush(transaction);
|
await em.persistAndFlush(transaction);
|
||||||
|
|
||||||
return transaction;
|
return transaction;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user