reservation implementation

This commit is contained in:
root
2026-06-11 15:35:25 -04:00
parent 6eeba99c45
commit 37a6ed8a76
30 changed files with 2598 additions and 477 deletions
+18 -27
View File
@@ -1,10 +1,8 @@
import { PLAN_FEATURES, PLAN_PRICES } from '@rentaldrivego/types'
import { AppError, NotFoundError } from '../../http/errors'
import { getVehicleAvailabilitySummary } from '../../services/vehicleAvailabilityService'
import { applyInsurancesToReservation } from '../../services/insuranceService'
import { applyAdditionalDriversToReservation } from '../../services/additionalDriverService'
import { applyPricingRules } from '../../services/pricingRuleService'
import { validateLicense, validateAndFlagLicense } from '../../services/licenseValidationService'
import { validateLicense } from '../../services/licenseValidationService'
import { getMarketplaceHomepageContent } from '../../services/platformContentService'
import { prisma } from '../../lib/prisma'
import * as amanpay from '../../services/amanpayService'
@@ -48,6 +46,12 @@ function assertAllowedPaymentRedirect(urlValue: string, company: any) {
}
}
function generateBookingReference() {
const year = new Date().getUTCFullYear()
const random = Math.random().toString(36).slice(2, 8).toUpperCase()
return `BK-${year}-${random}`
}
export async function getPlatformHomepage() {
return getMarketplaceHomepageContent()
}
@@ -132,8 +136,9 @@ export async function validatePromoCode(slug: string, code: string) {
export async function createBooking(slug: string, body: {
vehicleId: string; startDate: string; endDate: string
firstName: string; lastName: string; email: string; phone: string
driverLicense: string; dateOfBirth: string; licenseExpiry: string; licenseIssuedAt: string; nationality: string
identityDocumentNumber: string; fullAddress: string; licenseCountry: string; licenseNumber?: string; licenseCategory: string; internationalLicenseNumber?: string
pickupLocation: string; returnLocation: string
driverLicense?: string; dateOfBirth?: string; licenseExpiry?: string; licenseIssuedAt?: string; nationality?: string
identityDocumentNumber?: string; fullAddress?: string; licenseCountry?: string; licenseNumber?: string; licenseCategory?: string; internationalLicenseNumber?: string
offerId?: string; promoCodeUsed?: string; notes?: string; source?: string
selectedInsurancePolicyIds?: string[]; additionalDrivers?: any[]
}) {
@@ -143,6 +148,7 @@ export async function createBooking(slug: string, body: {
const start = new Date(body.startDate)
const end = new Date(body.endDate)
if (end <= start) throw new AppError('End date must be after start date', 400, 'invalid_dates')
if (start < new Date()) throw new AppError('Start date cannot be in the past', 400, 'invalid_dates')
const availability = await getVehicleAvailabilitySummary(vehicle.id, { companyId: company.id, range: { startDate: start, endDate: end } })
if (!availability.available) {
@@ -182,16 +188,10 @@ export async function createBooking(slug: string, body: {
}
}
const additionalDriversList = body.additionalDrivers ?? []
const { applied, total: pricingRulesTotal } = await applyPricingRules(
company.id, customer.id, additionalDriversList as any[], vehicle.dailyRate, totalDays,
company.id, customer.id, [], vehicle.dailyRate, totalDays,
)
const primaryLicenseResult = validateLicense(body.licenseExpiry ? new Date(body.licenseExpiry) : null)
if (primaryLicenseResult.status === 'EXPIRED') {
throw new AppError('The primary driver license is expired', 400, 'license_expired')
}
const reservation = await repo.createReservation({
companyId: company.id,
vehicleId: vehicle.id,
@@ -202,6 +202,8 @@ export async function createBooking(slug: string, body: {
source: body.source ?? 'PUBLIC_SITE',
startDate: start,
endDate: end,
pickupLocation: body.pickupLocation,
returnLocation: body.returnLocation,
dailyRate: vehicle.dailyRate,
totalDays,
totalAmount: baseAmount - discountAmount + pricingRulesTotal,
@@ -209,20 +211,10 @@ export async function createBooking(slug: string, body: {
pricingRulesApplied: applied,
pricingRulesTotal,
notes: body.notes ?? null,
bookingReference: generateBookingReference(),
status: 'DRAFT',
})
const insuranceIds = body.selectedInsurancePolicyIds ?? []
if (insuranceIds.length > 0) {
await applyInsurancesToReservation(reservation.id, company.id, insuranceIds, totalDays, baseAmount)
}
if (additionalDriversList.length > 0) {
await applyAdditionalDriversToReservation(reservation.id, company.id, additionalDriversList, totalDays)
}
if (body.licenseExpiry) {
await validateAndFlagLicense(customer.id).catch(() => null)
}
const publicAccess = generatePublicAccessToken()
await repo.createReservationPublicAccess(
reservation.id,
@@ -232,11 +224,10 @@ export async function createBooking(slug: string, body: {
const refreshed = await repo.findReservationWithDetails(reservation.id)
return {
...refreshed,
...presentPublicBooking({ ...refreshed, company }),
publicAccessToken: publicAccess.token,
requiresManualApproval:
primaryLicenseResult.requiresApproval ||
refreshed.additionalDrivers.some((d: any) => d.requiresApproval),
bookingReference: (refreshed as any).bookingReference,
message: 'The company will review your request.',
}
}