feat: ( bookings ) search backoffice bookings by provider transaction ID

This commit is contained in:
Abubeker Yasin
2026-08-03 16:56:36 +03:00
parent eea12bb57a
commit 6101a377f1
5 changed files with 69 additions and 16 deletions

View File

@@ -152,6 +152,12 @@ export class BookingsController {
@ApiQuery({ name: "returnLegStatus", required: false })
@ApiQuery({ name: "bookingType", required: false })
@ApiQuery({ name: "paymentStatus", required: false })
@ApiQuery({
name: "providerTxnId",
required: false,
description:
"Payment provider transaction / order / merchant reference (partial, case-insensitive)",
})
@ApiQuery({ name: "dateFrom", required: false })
@ApiQuery({ name: "dateTo", required: false })
@ApiQuery({ name: "page", required: false })
@@ -162,6 +168,7 @@ export class BookingsController {
@Query("returnLegStatus") returnLegStatus?: string,
@Query("bookingType") bookingType?: string,
@Query("paymentStatus") paymentStatus?: string,
@Query("providerTxnId") providerTxnId?: string,
@Query("dateFrom") dateFrom?: string,
@Query("dateTo") dateTo?: string,
@Query("page") page?: string,
@@ -173,6 +180,7 @@ export class BookingsController {
returnLegStatus,
bookingType,
paymentStatus,
providerTxnId,
dateFrom,
dateTo,
page: page ? parseInt(page) : 1,

View File

@@ -91,6 +91,7 @@ interface BookingFilters {
returnLegStatus?: string;
bookingType?: string;
paymentStatus?: string;
providerTxnId?: string;
dateFrom?: string;
dateTo?: string;
page?: number;
@@ -453,8 +454,9 @@ export class BookingsService {
}
async findAll(filters: BookingFilters = {}) {
const { search, status, returnLegStatus, bookingType, paymentStatus, dateFrom, dateTo, page = 1, pageSize = 20 } = filters;
const { search, status, returnLegStatus, bookingType, paymentStatus, providerTxnId, dateFrom, dateTo, page = 1, pageSize = 20 } = filters;
const skip = (page - 1) * pageSize;
const txn = providerTxnId?.trim() || undefined;
const onlyPackages = bookingType === 'PACKAGE';
const includePackageBookings = !returnLegStatus && bookingType !== 'ONE_WAY' && bookingType !== 'ROUND_TRIP' && bookingType !== 'TRANSIT' && bookingType !== 'ROUND_TRIP_TRANSIT';
@@ -495,11 +497,24 @@ export class BookingsService {
...(dateTo ? { lte: new Date(new Date(dateTo).setHours(23, 59, 59, 999)) } : {}),
};
}
// paymentStatus and providerTxnId both narrow the same relation — build one `is` filter
// so the second doesn't overwrite the first.
const paymentIntentIs: any = {};
if (paymentStatus) {
const statusMap: Record<string, string> = { PAID: 'SUCCEEDED', PENDING: 'REQUIRES_ACTION', FAILED: 'FAILED', REFUNDED: 'REFUNDED' };
const mapped = statusMap[paymentStatus] ?? paymentStatus;
where.paymentIntent = { is: { status: mapped } };
paymentIntentIs.status = statusMap[paymentStatus] ?? paymentStatus;
}
if (txn) {
// Providers are inconsistent about which reference they hand back to the customer —
// match the transaction id, the provider/merchant order ids, and the generic ref.
paymentIntentIs.OR = [
{ providerTxnId: { contains: txn, mode: 'insensitive' } },
{ providerOrderId: { contains: txn, mode: 'insensitive' } },
{ merchantOrderId: { contains: txn, mode: 'insensitive' } },
{ providerRef: { contains: txn, mode: 'insensitive' } },
];
}
if (Object.keys(paymentIntentIs).length) where.paymentIntent = { is: paymentIntentIs };
const pkgWhere: any = {};
if (search) {
@@ -512,7 +527,12 @@ export class BookingsService {
}
if (status) pkgWhere.status = status;
if (dateFrom || dateTo) pkgWhere.createdAt = where.createdAt;
if (paymentStatus) pkgWhere.paymentIntent = { is: { status: (where.paymentIntent as any)?.is?.status } };
const pkgPaymentIntentIs: any = {};
if (paymentStatus) pkgPaymentIntentIs.status = paymentIntentIs.status;
// PackagePaymentIntent has no providerTxnId/providerOrderId/merchantOrderId columns —
// providerRef is the only reference we can match a package booking on.
if (txn) pkgPaymentIntentIs.providerRef = { contains: txn, mode: 'insensitive' };
if (Object.keys(pkgPaymentIntentIs).length) pkgWhere.paymentIntent = { is: pkgPaymentIntentIs };
if (onlyPackages) {
// Package bookings live in two places:
@@ -521,7 +541,7 @@ export class BookingsService {
const bookingPkgWhere: any = { packageId: { not: null } };
if (status) bookingPkgWhere.status = status;
if (dateFrom || dateTo) bookingPkgWhere.createdAt = where.createdAt;
if (paymentStatus) bookingPkgWhere.paymentIntent = where.paymentIntent;
if (where.paymentIntent) bookingPkgWhere.paymentIntent = where.paymentIntent;
if (search) bookingPkgWhere.OR = where.OR;
const [pkgItems, pkgTotal, regPkgItems, regPkgTotal] = await Promise.all([