102 lines
3.0 KiB
TypeScript
102 lines
3.0 KiB
TypeScript
/**
|
|
* Backfill Booking + Payment dari TripParticipant lama.
|
|
*
|
|
* Idempotent — jalan ulang aman. Skip baris yang sudah punya Booking.
|
|
*
|
|
* Mapping:
|
|
* - participant.status === "CANCELLED" → Booking CANCELLED, no Payment
|
|
* - participant.status === "PENDING" → Booking PENDING, no Payment
|
|
* - participant.status === "CONFIRMED" + free → Booking PAID, no Payment
|
|
* - participant.status === "CONFIRMED" + paid:
|
|
* - paymentConfirmedAt set → Booking PAID, Payment PAID (paidAt = paymentConfirmedAt)
|
|
* - markedPaidAt set, no confirm → Booking AWAITING_PAY, Payment AWAITING
|
|
* - neither → Booking AWAITING_PAY, no Payment
|
|
*
|
|
* Jalankan: `npx tsx prisma/backfill-bookings.ts`
|
|
*/
|
|
import { PrismaClient, Prisma } from "../app/generated/prisma/client";
|
|
import { PrismaPg } from "@prisma/adapter-pg";
|
|
|
|
const adapter = new PrismaPg({
|
|
connectionString: process.env.DATABASE_URL!,
|
|
});
|
|
const prisma = new PrismaClient({ adapter });
|
|
|
|
async function main() {
|
|
const participants = await prisma.tripParticipant.findMany({
|
|
include: {
|
|
trip: { select: { price: true } },
|
|
booking: { select: { id: true } },
|
|
},
|
|
orderBy: { createdAt: "asc" },
|
|
});
|
|
|
|
let createdBookings = 0;
|
|
let createdPayments = 0;
|
|
let skipped = 0;
|
|
|
|
for (const p of participants) {
|
|
if (p.booking) {
|
|
skipped++;
|
|
continue;
|
|
}
|
|
|
|
const isFree = p.trip.price <= 0;
|
|
|
|
let bookingStatus: Prisma.BookingCreateInput["status"];
|
|
if (p.status === "CANCELLED") {
|
|
bookingStatus = "CANCELLED";
|
|
} else if (p.status === "PENDING") {
|
|
bookingStatus = "PENDING";
|
|
} else if (isFree) {
|
|
bookingStatus = "PAID";
|
|
} else if (p.paymentConfirmedAt) {
|
|
bookingStatus = "PAID";
|
|
} else {
|
|
bookingStatus = "AWAITING_PAY";
|
|
}
|
|
|
|
const booking = await prisma.booking.create({
|
|
data: {
|
|
tripId: p.tripId,
|
|
userId: p.userId,
|
|
participantId: p.id,
|
|
amount: p.trip.price,
|
|
status: bookingStatus,
|
|
},
|
|
});
|
|
createdBookings++;
|
|
|
|
// Payment row hanya kalau ada jejak pembayaran manual
|
|
if (!isFree && (p.markedPaidAt || p.paymentConfirmedAt)) {
|
|
const paymentStatus: Prisma.PaymentCreateInput["status"] =
|
|
p.paymentConfirmedAt ? "PAID" : "AWAITING";
|
|
await prisma.payment.create({
|
|
data: {
|
|
bookingId: booking.id,
|
|
provider: "MANUAL",
|
|
externalOrderId: `manual-${booking.id}`,
|
|
amount: p.trip.price,
|
|
status: paymentStatus,
|
|
method: "manual_transfer",
|
|
paidAt: p.paymentConfirmedAt ?? null,
|
|
},
|
|
});
|
|
createdPayments++;
|
|
}
|
|
}
|
|
|
|
console.log(
|
|
`✅ Backfill selesai. Booking dibuat: ${createdBookings}, Payment dibuat: ${createdPayments}, dilewati (sudah ada): ${skipped}`
|
|
);
|
|
}
|
|
|
|
main()
|
|
.catch((e) => {
|
|
console.error("❌ Backfill gagal:", e);
|
|
process.exit(1);
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect();
|
|
});
|