feat: update refund ticket handling and enhance service schemas
- Removed the taskRefundTicket method from CronService to streamline scheduling logic. - Updated PayosService to include MessageContextType for chat messages, improving message context handling. - Enhanced RefundTicketSchema with additional fields for requester and improved error handling for unauthorized access. - Implemented logic to update order and schedule statuses upon refund ticket approval, ensuring proper state management. - Modified ServiceSchema to utilize createManyAndReturn for message creation, enhancing notification efficiency. These changes improve the handling of refund tickets and enhance the overall functionality of the service schemas, ensuring better user experience and state management.
This commit is contained in:
Submodule epess-database updated: fde5cb205b...793a9ad7ac
@@ -99,51 +99,6 @@ export class CronService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle refund ticket by order, if order status is refunded, disable schedule and remove schedule date in future
|
|
||||||
@Cron(CronExpression.EVERY_MINUTE)
|
|
||||||
async taskRefundTicket() {
|
|
||||||
Logger.log('Handling refund ticket', 'CronService')
|
|
||||||
const now = DateTimeUtils.now().toJSDate()
|
|
||||||
// get all orders where status is REFUNDED and has schedule.dates in future
|
|
||||||
const orders = await this.prisma.order.findMany({
|
|
||||||
where: {
|
|
||||||
status: OrderStatus.REFUNDED,
|
|
||||||
schedule: {
|
|
||||||
dates: {
|
|
||||||
some: {
|
|
||||||
end: {
|
|
||||||
gt: now,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
schedule: {
|
|
||||||
include: {
|
|
||||||
dates: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
Logger.log(`Found ${orders.length} orders to handle`, 'CronService')
|
|
||||||
for (const order of orders) {
|
|
||||||
await this.prisma.schedule.update({
|
|
||||||
where: { id: order.scheduleId },
|
|
||||||
data: { status: ScheduleStatus.REFUNDED },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// remove schedule date in future
|
|
||||||
for (const order of orders) {
|
|
||||||
await this.prisma.scheduleDate.deleteMany({
|
|
||||||
where: {
|
|
||||||
id: { in: order.schedule.dates.map((d) => d.id) },
|
|
||||||
start: { gt: now },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// cron every 1 minute to check if there is any schedule date start in less than 30 minutes
|
// cron every 1 minute to check if there is any schedule date start in less than 30 minutes
|
||||||
@Cron(CronExpression.EVERY_MINUTE)
|
@Cron(CronExpression.EVERY_MINUTE)
|
||||||
async taskCheckScheduleDateStart() {
|
async taskCheckScheduleDateStart() {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import type {
|
|||||||
} from '@payos/node/lib/type'
|
} from '@payos/node/lib/type'
|
||||||
import {
|
import {
|
||||||
ChatRoomType,
|
ChatRoomType,
|
||||||
|
MessageContextType,
|
||||||
MessageType,
|
MessageType,
|
||||||
OrderStatus,
|
OrderStatus,
|
||||||
PaymentStatus,
|
PaymentStatus,
|
||||||
@@ -129,6 +130,7 @@ export class PayosService {
|
|||||||
type: MessageType.TEXT,
|
type: MessageType.TEXT,
|
||||||
chatRoomId: chatRoom.id,
|
chatRoomId: chatRoom.id,
|
||||||
senderId: mentorId,
|
senderId: mentorId,
|
||||||
|
context: MessageContextType.CHAT,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common'
|
import { Inject, Injectable } from '@nestjs/common'
|
||||||
import { MessageContextType, MessageType, OrderStatus, PaymentStatus, RefundTicketStatus, Role } from '@prisma/client'
|
import {
|
||||||
|
MessageContextType,
|
||||||
|
MessageType,
|
||||||
|
OrderStatus,
|
||||||
|
PaymentStatus,
|
||||||
|
RefundTicketStatus,
|
||||||
|
Role,
|
||||||
|
ScheduleDateStatus,
|
||||||
|
ScheduleStatus,
|
||||||
|
} from '@prisma/client'
|
||||||
import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos'
|
import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos'
|
||||||
import { PubSubEvent } from 'src/common/pubsub/pubsub-event'
|
import { PubSubEvent } from 'src/common/pubsub/pubsub-event'
|
||||||
import { DateTimeUtils } from 'src/common/utils/datetime.utils'
|
import { DateTimeUtils } from 'src/common/utils/datetime.utils'
|
||||||
@@ -56,6 +65,9 @@ export class RefundTicketSchema extends PothosSchema {
|
|||||||
order: t.relation('order', {
|
order: t.relation('order', {
|
||||||
description: 'The order for the refund ticket.',
|
description: 'The order for the refund ticket.',
|
||||||
}),
|
}),
|
||||||
|
requester: t.relation('requester', {
|
||||||
|
description: 'The requester of the refund ticket.',
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -123,7 +135,9 @@ export class RefundTicketSchema extends PothosSchema {
|
|||||||
if (ctx.isSubscription) {
|
if (ctx.isSubscription) {
|
||||||
throw new Error('Subscription is not allowed')
|
throw new Error('Subscription is not allowed')
|
||||||
}
|
}
|
||||||
|
if (!ctx.http.me) {
|
||||||
|
throw new Error('Unauthorized')
|
||||||
|
}
|
||||||
// Check if the user is a customer or a center mentor
|
// Check if the user is a customer or a center mentor
|
||||||
if (ctx.http.me?.role !== Role.CUSTOMER && ctx.http.me?.role !== Role.CENTER_MENTOR) {
|
if (ctx.http.me?.role !== Role.CUSTOMER && ctx.http.me?.role !== Role.CENTER_MENTOR) {
|
||||||
throw new Error('Only customers and center mentors can request refund')
|
throw new Error('Only customers and center mentors can request refund')
|
||||||
@@ -216,6 +230,7 @@ export class RefundTicketSchema extends PothosSchema {
|
|||||||
bankBin: bankBin,
|
bankBin: bankBin,
|
||||||
bankAccountNumber: bankAccountNumber,
|
bankAccountNumber: bankAccountNumber,
|
||||||
bankName: bankName,
|
bankName: bankName,
|
||||||
|
requesterId: ctx.http.me.id,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
// notify all Moderator
|
// notify all Moderator
|
||||||
@@ -272,9 +287,56 @@ export class RefundTicketSchema extends PothosSchema {
|
|||||||
data: {
|
data: {
|
||||||
status: args.action === 'APPROVE' ? RefundTicketStatus.APPROVED : RefundTicketStatus.REJECTED,
|
status: args.action === 'APPROVE' ? RefundTicketStatus.APPROVED : RefundTicketStatus.REJECTED,
|
||||||
rejectedReason: args.action === 'REJECT' ? args.reason : undefined,
|
rejectedReason: args.action === 'REJECT' ? args.reason : undefined,
|
||||||
moderatorId: ctx.http.me?.id,
|
moderatorId: ctx.http.me.id,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
order: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
// update order status
|
||||||
|
await this.prisma.order.update({
|
||||||
|
where: { id: refundTicket.orderId },
|
||||||
|
data: {
|
||||||
|
status: OrderStatus.REFUNDED,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// change schedule status
|
||||||
|
await this.prisma.schedule.update({
|
||||||
|
where: { id: refundTicket.order.scheduleId },
|
||||||
|
data: {
|
||||||
|
status: ScheduleStatus.REFUNDED,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// change schedule date status in future
|
||||||
|
await this.prisma.scheduleDate.updateMany({
|
||||||
|
where: {
|
||||||
|
scheduleId: refundTicket.order.scheduleId,
|
||||||
|
start: {
|
||||||
|
gt: DateTimeUtils.nowAsJSDate(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: ScheduleDateStatus.EXPIRED,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// send notification to requester
|
||||||
|
const requester = await this.prisma.user.findUnique({
|
||||||
|
where: { id: refundTicket.requesterId },
|
||||||
|
})
|
||||||
|
if (!requester) {
|
||||||
|
throw new Error('Requester not found')
|
||||||
|
}
|
||||||
|
const message = await this.prisma.message.create({
|
||||||
|
data: {
|
||||||
|
senderId: ctx.http.me.id,
|
||||||
|
recipientId: requester.id,
|
||||||
|
type: MessageType.TEXT,
|
||||||
|
content: `Yêu cầu hoàn tiền của bạn đã được ${args.action === 'APPROVE' ? 'chấp thuận' : 'từ chối'}`,
|
||||||
|
sentAt: DateTimeUtils.nowAsJSDate(),
|
||||||
|
context: MessageContextType.NOTIFICATION,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
ctx.http.pubSub.publish(`${PubSubEvent.NOTIFICATION}.${requester.id}`, message)
|
||||||
return refundTicket
|
return refundTicket
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ export class ServiceSchema extends PothosSchema {
|
|||||||
const moderatorIds = await this.prisma.user.findMany({
|
const moderatorIds = await this.prisma.user.findMany({
|
||||||
where: { role: Role.MODERATOR },
|
where: { role: Role.MODERATOR },
|
||||||
})
|
})
|
||||||
const messages = await this.prisma.message.createMany({
|
const messages = await this.prisma.message.createManyAndReturn({
|
||||||
data: moderatorIds.map((moderator) => ({
|
data: moderatorIds.map((moderator) => ({
|
||||||
senderId: ctx.http.me?.id ?? '',
|
senderId: ctx.http.me?.id ?? '',
|
||||||
recipientId: moderator.id,
|
recipientId: moderator.id,
|
||||||
@@ -287,8 +287,8 @@ export class ServiceSchema extends PothosSchema {
|
|||||||
context: MessageContextType.NOTIFICATION,
|
context: MessageContextType.NOTIFICATION,
|
||||||
})),
|
})),
|
||||||
})
|
})
|
||||||
moderatorIds.forEach((moderator) => {
|
messages.forEach((message) => {
|
||||||
ctx.http.pubSub.publish(`${PubSubEvent.NOTIFICATION}.${moderator.id}`, messages)
|
ctx.http.pubSub.publish(`${PubSubEvent.NOTIFICATION}.${message.recipientId}`, message)
|
||||||
})
|
})
|
||||||
return service
|
return service
|
||||||
},
|
},
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user