refund roadmap pr-1 and pr-2

This commit is contained in:
2026-05-11 13:04:20 +07:00
parent d2b0a780d5
commit 54f4569107
36 changed files with 5750 additions and 19 deletions
+13
View File
@@ -71,4 +71,17 @@ export const bookingRepo = {
data: { status },
});
},
/**
* Jumlah booking PAID/PARTIALLY_REFUNDED di trip. Dipakai untuk preview
* dampak cancel-trip (berapa peserta yang akan dapat auto-refund).
*/
async countSettledForTrip(tripId: string) {
return prisma.booking.count({
where: {
tripId,
status: { in: ["PAID", "PARTIALLY_REFUNDED"] },
},
});
},
};
+111
View File
@@ -0,0 +1,111 @@
import { prisma } from "@/lib/prisma";
import { Prisma } from "@/app/generated/prisma/client";
const refundListInclude = {
booking: {
include: {
trip: { select: { id: true, title: true, date: true, organizerId: true } },
user: { select: { id: true, name: true, email: true, image: true } },
payments: {
orderBy: { createdAt: "desc" },
select: {
id: true,
provider: true,
method: true,
amount: true,
status: true,
paidAt: true,
},
},
},
},
payment: {
select: {
id: true,
provider: true,
method: true,
amount: true,
status: true,
},
},
reviewedBy: { select: { id: true, name: true, email: true } },
} satisfies Prisma.RefundInclude;
export const refundRepo = {
async findById(id: string) {
return prisma.refund.findUnique({
where: { id },
include: refundListInclude,
});
},
async listByStatus(
status?: "PENDING" | "APPROVED" | "REJECTED" | "PROCESSING" | "SUCCEEDED" | "FAILED"
) {
return prisma.refund.findMany({
where: status ? { status } : undefined,
orderBy: { createdAt: "desc" },
include: refundListInclude,
});
},
async listByBooking(bookingId: string) {
return prisma.refund.findMany({
where: { bookingId },
orderBy: { createdAt: "desc" },
});
},
/** Total nominal yang sudah SUCCEEDED untuk satu booking. Dipakai service untuk
* validasi `SUM(SUCCEEDED) + new.amount <= payment.amount`. */
async sumSucceededAmount(bookingId: string, tx?: Prisma.TransactionClient): Promise<number> {
const client = tx ?? prisma;
const agg = await client.refund.aggregate({
where: { bookingId, status: "SUCCEEDED" },
_sum: { amount: true },
});
return agg._sum.amount ?? 0;
},
/** Pending + approved + processing — refund yang "in-flight" (belum settled).
* Dipakai untuk cek apakah booking masih punya refund aktif. */
async hasActiveRefund(bookingId: string, tx?: Prisma.TransactionClient): Promise<boolean> {
const client = tx ?? prisma;
const count = await client.refund.count({
where: {
bookingId,
status: { in: ["PENDING", "APPROVED", "PROCESSING"] },
},
});
return count > 0;
},
async create(
data: Pick<
Prisma.RefundUncheckedCreateInput,
| "bookingId"
| "paymentId"
| "amount"
| "reason"
| "reportedBy"
| "reportNote"
| "initiatedBy"
| "idempotencyKey"
>,
tx?: Prisma.TransactionClient
) {
const client = tx ?? prisma;
return client.refund.create({ data });
},
async update(
id: string,
data: Prisma.RefundUncheckedUpdateInput,
tx?: Prisma.TransactionClient
) {
const client = tx ?? prisma;
return client.refund.update({ where: { id }, data });
},
};
export type RefundWithRelations = Awaited<ReturnType<typeof refundRepo.findById>>;