add booking and payment schema
This commit is contained in:
@@ -3,6 +3,8 @@ import type { ActivityCategory, Vibe } from "@/app/generated/prisma/enums";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { tripRepo, type TripFilters } from "@/server/repositories/trip.repo";
|
||||
import { participantRepo } from "@/server/repositories/participant.repo";
|
||||
import { bookingRepo } from "@/server/repositories/booking.repo";
|
||||
import { bookingService } from "@/server/services/booking.service";
|
||||
import { LIMITS } from "@/lib/limits";
|
||||
import { utcStartOfDay, isTripDepartureDayPast } from "@/lib/trip-dates";
|
||||
import { isFreeTrip } from "@/lib/trip-pricing";
|
||||
@@ -140,6 +142,7 @@ export const tripService = {
|
||||
date: true,
|
||||
organizerId: true,
|
||||
maxParticipants: true,
|
||||
price: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -186,6 +189,22 @@ export const tripService = {
|
||||
data: { tripId, userId, status: "PENDING" },
|
||||
});
|
||||
|
||||
// Booking 1-1 ke participant. Upsert untuk handle re-join setelah CANCELLED.
|
||||
await tx.booking.upsert({
|
||||
where: { participantId: participant.id },
|
||||
create: {
|
||||
tripId,
|
||||
userId,
|
||||
participantId: participant.id,
|
||||
amount: trip.price,
|
||||
status: "PENDING",
|
||||
},
|
||||
update: {
|
||||
status: "PENDING",
|
||||
amount: trip.price,
|
||||
},
|
||||
});
|
||||
|
||||
const newCount = await tx.tripParticipant.count({
|
||||
where: { tripId, status: { not: "CANCELLED" } },
|
||||
});
|
||||
@@ -234,7 +253,21 @@ export const tripService = {
|
||||
throw new Error("Kamu tidak terdaftar di trip ini");
|
||||
}
|
||||
|
||||
const result = await participantRepo.cancel(tripId, userId);
|
||||
const result = await prisma.$transaction(async (tx) => {
|
||||
const cancelled = await tx.tripParticipant.update({
|
||||
where: { tripId_userId: { tripId, userId } },
|
||||
data: {
|
||||
status: "CANCELLED",
|
||||
markedPaidAt: null,
|
||||
paymentConfirmedAt: null,
|
||||
},
|
||||
});
|
||||
await tx.booking.updateMany({
|
||||
where: { participantId: existing.id },
|
||||
data: { status: "CANCELLED" },
|
||||
});
|
||||
return cancelled;
|
||||
});
|
||||
|
||||
if (trip.status === "FULL") {
|
||||
const count = await participantRepo.countByTrip(tripId);
|
||||
@@ -267,7 +300,19 @@ export const tripService = {
|
||||
throw new Error("Peserta ini tidak dalam status menunggu persetujuan");
|
||||
}
|
||||
|
||||
return participantRepo.setStatus(participantId, "CONFIRMED");
|
||||
// Trip gratis: Booking langsung PAID. Trip berbayar: AWAITING_PAY (tinggal bayar).
|
||||
const nextBookingStatus = isFreeTrip(trip) ? "PAID" : "AWAITING_PAY";
|
||||
|
||||
return prisma.$transaction(async (tx) => {
|
||||
await tx.booking.updateMany({
|
||||
where: { participantId },
|
||||
data: { status: nextBookingStatus },
|
||||
});
|
||||
return tx.tripParticipant.update({
|
||||
where: { id: participantId },
|
||||
data: { status: "CONFIRMED" },
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
async rejectParticipant(
|
||||
@@ -291,10 +336,20 @@ export const tripService = {
|
||||
throw new Error("Hanya permintaan yang masih menunggu yang bisa ditolak");
|
||||
}
|
||||
|
||||
await participantRepo.setStatusAndClearPayment(
|
||||
participantId,
|
||||
"CANCELLED"
|
||||
);
|
||||
await prisma.$transaction(async (tx) => {
|
||||
await tx.tripParticipant.update({
|
||||
where: { id: participantId },
|
||||
data: {
|
||||
status: "CANCELLED",
|
||||
markedPaidAt: null,
|
||||
paymentConfirmedAt: null,
|
||||
},
|
||||
});
|
||||
await tx.booking.updateMany({
|
||||
where: { participantId },
|
||||
data: { status: "CANCELLED" },
|
||||
});
|
||||
});
|
||||
|
||||
if (trip.status === "FULL") {
|
||||
const count = await participantRepo.countByTrip(tripId);
|
||||
@@ -322,40 +377,12 @@ export const tripService = {
|
||||
throw new Error("Trip sudah lewat — pembayaran tidak perlu ditandai");
|
||||
}
|
||||
|
||||
const p = await participantRepo.findByTripAndUser(tripId, userId);
|
||||
if (!p || p.status === "CANCELLED") {
|
||||
const booking = await bookingRepo.findByTripAndUser(tripId, userId);
|
||||
if (!booking || booking.status === "CANCELLED") {
|
||||
throw new Error("Kamu tidak terdaftar di trip ini");
|
||||
}
|
||||
|
||||
if (p.paymentConfirmedAt) {
|
||||
throw new Error("Pembayaran kamu sudah dikonfirmasi organizer");
|
||||
}
|
||||
if (p.markedPaidAt) {
|
||||
throw new Error("Kamu sudah menandai sudah bayar — tunggu konfirmasi organizer");
|
||||
}
|
||||
|
||||
const updated = await participantRepo.tryMarkPaidByUser(tripId, userId);
|
||||
if (updated.count === 0) {
|
||||
const again = await participantRepo.findByTripAndUser(tripId, userId);
|
||||
if (!again || again.status === "CANCELLED") {
|
||||
throw new Error("Kamu tidak terdaftar di trip ini");
|
||||
}
|
||||
if (again.paymentConfirmedAt) {
|
||||
throw new Error("Pembayaran kamu sudah dikonfirmasi organizer");
|
||||
}
|
||||
if (again.markedPaidAt) {
|
||||
throw new Error(
|
||||
"Kamu sudah menandai sudah bayar — tunggu konfirmasi organizer"
|
||||
);
|
||||
}
|
||||
throw new Error("Tidak bisa menandai pembayaran. Coba lagi sebentar.");
|
||||
}
|
||||
|
||||
const row = await participantRepo.findByTripAndUser(tripId, userId);
|
||||
if (!row) {
|
||||
throw new Error("Data peserta tidak ditemukan setelah update");
|
||||
}
|
||||
return row;
|
||||
return bookingService.markPaidManual(booking.id, userId);
|
||||
},
|
||||
|
||||
async confirmParticipantPayment(
|
||||
@@ -374,29 +401,11 @@ export const tripService = {
|
||||
throw new Error("Trip ini gratis — tidak ada pembayaran yang perlu dikonfirmasi");
|
||||
}
|
||||
|
||||
const participant = await participantRepo.findById(participantId);
|
||||
if (!participant || participant.tripId !== tripId) {
|
||||
throw new Error("Peserta tidak ditemukan");
|
||||
}
|
||||
if (participant.status === "CANCELLED") {
|
||||
throw new Error("Peserta sudah tidak aktif");
|
||||
}
|
||||
if (!participant.markedPaidAt) {
|
||||
throw new Error("Peserta belum menandai sudah membayar");
|
||||
}
|
||||
if (participant.paymentConfirmedAt) {
|
||||
throw new Error("Pembayaran peserta ini sudah dikonfirmasi");
|
||||
const booking = await bookingRepo.findByParticipantId(participantId);
|
||||
if (!booking || booking.tripId !== tripId) {
|
||||
throw new Error("Booking tidak ditemukan");
|
||||
}
|
||||
|
||||
const updated = await participantRepo.tryConfirmPaymentByOrganizer(
|
||||
participantId
|
||||
);
|
||||
if (updated.count === 0) {
|
||||
throw new Error(
|
||||
"Konfirmasi tidak diproses — mungkin sudah dikonfirmasi atau pembayaran belum ditandai peserta."
|
||||
);
|
||||
}
|
||||
|
||||
return participantRepo.findById(participantId);
|
||||
return bookingService.confirmPaidManual(booking.id, organizerId);
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user