Files
epess-web-backend/src/RefundTicket/refundticket.schema.ts
2024-11-26 21:02:14 +07:00

178 lines
6.3 KiB
TypeScript

import { Inject, Injectable } from '@nestjs/common'
import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos'
import { Builder } from '../Graphql/graphql.builder'
import { PrismaService } from '../Prisma/prisma.service'
import { OrderStatus, PaymentStatus, RefundTicketStatus, Role } from '@prisma/client'
import { DateTimeUtils } from 'src/common/utils/datetime.utils'
@Injectable()
export class RefundTicketSchema extends PothosSchema {
constructor(
@Inject(SchemaBuilderToken) private readonly builder: Builder,
private readonly prisma: PrismaService,
) {
super()
}
// Types section
@PothosRef()
refundTicket() {
return this.builder.prismaObject('RefundTicket', {
fields: (t) => ({
id: t.exposeID('id', {
description: 'The ID of the refund ticket.',
}),
amount: t.exposeFloat('amount', {
description: 'The amount of the refund ticket.',
}),
status: t.expose('status', {
type: RefundTicketStatus,
description: 'The status of the refund ticket.',
}),
createdAt: t.expose('createdAt', {
type: 'DateTime',
description: 'The date and time the refund ticket was created.',
}),
updatedAt: t.expose('updatedAt', {
type: 'DateTime',
description: 'The date and time the refund ticket was updated.',
}),
order: t.relation('order', {
description: 'The order for the refund ticket.',
}),
}),
})
}
@PothosRef()
refundTicketAction() {
return this.builder.enumType('RefundTicketAction', {
description: 'The action to take on a refund ticket.',
values: ['APPROVE', 'REJECT'],
})
}
// Queries section
@Pothos()
init(): void {
this.builder.queryFields((t) => ({
refundTicket: t.prismaField({
type: this.refundTicket(),
description: 'Retrieve a refund ticket by ID.',
args: {
id: t.arg({ type: 'String', required: true }),
},
resolve: async (query, _root, args, ctx, _info) => {
if (ctx.isSubscription) {
throw new Error('Subscription is not allowed')
}
if (ctx.http.me?.role !== Role.MODERATOR) {
throw new Error('Only moderators can retrieve refund tickets')
}
return await this.prisma.refundTicket.findUnique({ ...query, where: { id: args.id } })
},
}),
refundTickets: t.prismaField({
type: [this.refundTicket()],
description: 'Retrieve a list of refund tickets with optional filtering, ordering, and pagination.',
args: this.builder.generator.findManyArgs('RefundTicket'),
resolve: async (query, _root, args, _ctx, _info) => {
return await this.prisma.refundTicket.findMany({
...query,
where: args.filter ?? undefined,
orderBy: args.orderBy ?? undefined,
cursor: args.cursor ?? undefined,
take: args.take ?? undefined,
skip: args.skip ?? undefined,
})
},
}),
}))
this.builder.mutationFields((t) => ({
requestRefund: t.prismaField({
type: this.refundTicket(),
description: 'Request a refund for an order.',
args: {
orderId: t.arg({
type: 'String',
required: true,
}),
reason: t.arg({
type: 'String',
required: true,
}),
},
resolve: async (_query, _root, args, ctx, _info) => {
if (ctx.isSubscription) {
throw new Error('Subscription is not allowed')
}
if (ctx.http.me?.role !== Role.CUSTOMER) {
throw new Error('Only customers can request refund')
}
// check if order exists
const order = await this.prisma.order.findUnique({
where: { id: args.orderId },
})
if (!order) {
throw new Error('Order not found')
}
// check if order status is PAID
if (order.status !== OrderStatus.PAID) {
throw new Error('Order is not paid')
}
// check if order total is not null
if (!order.total || order.total === 0) {
throw new Error('Order total is null or free')
}
// calculate refund amount based on order time: if order is less than 24 hours, refund 100%, if more than 24 hours, less than 48 hours, refund 50%, if more than 72 hours, cannot refund
const now = DateTimeUtils.now()
const orderDate = DateTimeUtils.fromDate(order.createdAt)
const diffTime = Math.abs(now.diff(orderDate).toMillis())
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
let refundAmount = 0
if (diffDays < 24) refundAmount = order.total
else if (diffDays < 48) refundAmount = order.total * 0.5
if (refundAmount === 0) throw new Error('Cannot refund after 72 hours')
// create refund ticket
const refundTicket = await this.prisma.refundTicket.create({
data: {
orderId: order.id,
status: RefundTicketStatus.PENDING,
amount: refundAmount,
},
})
return refundTicket
},
}),
processRefundTicket: t.prismaField({
type: this.refundTicket(),
description: 'Process a refund ticket, can only done by moderator',
args: {
refundTicketId: t.arg({
type: 'String',
required: true,
}),
action: t.arg({
type: this.refundTicketAction(),
required: true,
}),
},
resolve: async (_query, _root, args, ctx, _info) => {
if (ctx.isSubscription) {
throw new Error('Subscription is not allowed')
}
if (ctx.http.me?.role !== Role.MODERATOR) {
throw new Error('Only moderators can process refund tickets')
}
// update refund ticket status
const refundTicket = await this.prisma.refundTicket.update({
where: { id: args.refundTicketId },
data: { status: args.action === 'APPROVE' ? RefundTicketStatus.APPROVED : RefundTicketStatus.REJECTED },
})
return refundTicket
},
}),
}))
}
}