111 lines
4.3 KiB
JavaScript
111 lines
4.3 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.getVehicleAvailabilitySummary = getVehicleAvailabilitySummary;
|
|
const prisma_1 = require("../lib/prisma");
|
|
const BLOCKING_RESERVATION_STATUSES = ['DRAFT', 'CONFIRMED', 'ACTIVE'];
|
|
function startOfTodayUtc() {
|
|
const now = new Date();
|
|
return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
|
}
|
|
function overlaps(range, source) {
|
|
return source.startDate < range.endDate && source.endDate > range.startDate;
|
|
}
|
|
function getNextAvailableAt(anchor, sources) {
|
|
const sorted = sources
|
|
.filter((source) => source.endDate > anchor)
|
|
.sort((a, b) => {
|
|
const startDelta = a.startDate.getTime() - b.startDate.getTime();
|
|
if (startDelta !== 0)
|
|
return startDelta;
|
|
return a.endDate.getTime() - b.endDate.getTime();
|
|
});
|
|
let candidate = anchor;
|
|
let advanced = false;
|
|
for (const source of sorted) {
|
|
if (source.endDate <= candidate)
|
|
continue;
|
|
if (source.startDate > candidate)
|
|
break;
|
|
candidate = new Date(Math.max(candidate.getTime(), source.endDate.getTime()));
|
|
advanced = true;
|
|
}
|
|
return advanced ? candidate : null;
|
|
}
|
|
async function getVehicleAvailabilitySummary(vehicleId, { range, includeDraftReservations = true, companyId, } = {}) {
|
|
const vehicle = await prisma_1.prisma.vehicle.findFirstOrThrow({
|
|
where: { id: vehicleId, ...(companyId ? { companyId } : {}) },
|
|
select: { status: true },
|
|
});
|
|
if (vehicle.status === 'OUT_OF_SERVICE') {
|
|
return {
|
|
available: false,
|
|
status: 'UNAVAILABLE',
|
|
nextAvailableAt: null,
|
|
blockingReason: 'STATUS',
|
|
};
|
|
}
|
|
const anchor = range?.startDate ?? startOfTodayUtc();
|
|
const reservationStatuses = includeDraftReservations
|
|
? [...BLOCKING_RESERVATION_STATUSES]
|
|
: BLOCKING_RESERVATION_STATUSES.filter((status) => status !== 'DRAFT');
|
|
const [reservations, blocks] = await Promise.all([
|
|
prisma_1.prisma.reservation.findMany({
|
|
where: {
|
|
vehicleId,
|
|
status: { in: reservationStatuses },
|
|
endDate: { gt: anchor },
|
|
},
|
|
select: { startDate: true, endDate: true },
|
|
orderBy: { startDate: 'asc' },
|
|
}),
|
|
prisma_1.prisma.vehicleCalendarBlock.findMany({
|
|
where: {
|
|
vehicleId,
|
|
endDate: { gt: anchor },
|
|
},
|
|
select: { startDate: true, endDate: true, type: true },
|
|
orderBy: { startDate: 'asc' },
|
|
}),
|
|
]);
|
|
const sources = [
|
|
...reservations.map((reservation) => ({
|
|
startDate: reservation.startDate,
|
|
endDate: reservation.endDate,
|
|
kind: 'RESERVATION',
|
|
})),
|
|
...blocks.map((block) => ({
|
|
startDate: block.startDate,
|
|
endDate: block.endDate,
|
|
kind: block.type === 'MAINTENANCE' ? 'MAINTENANCE' : 'BLOCK',
|
|
})),
|
|
];
|
|
const overlappingSources = range ? sources.filter((source) => overlaps(range, source)) : sources.filter((source) => source.startDate <= anchor && source.endDate > anchor);
|
|
const currentSource = overlappingSources.sort((a, b) => {
|
|
const priority = { MAINTENANCE: 0, BLOCK: 1, RESERVATION: 2 };
|
|
return priority[a.kind] - priority[b.kind];
|
|
})[0];
|
|
if (vehicle.status === 'MAINTENANCE') {
|
|
return {
|
|
available: false,
|
|
status: 'MAINTENANCE',
|
|
nextAvailableAt: getNextAvailableAt(anchor, sources) ?? null,
|
|
blockingReason: currentSource?.kind ?? 'STATUS',
|
|
};
|
|
}
|
|
if (!currentSource) {
|
|
return {
|
|
available: true,
|
|
status: 'AVAILABLE',
|
|
nextAvailableAt: null,
|
|
blockingReason: null,
|
|
};
|
|
}
|
|
const nextAvailableAnchor = currentSource.startDate > anchor ? currentSource.startDate : anchor;
|
|
return {
|
|
available: false,
|
|
status: currentSource.kind === 'RESERVATION' ? 'RESERVED' : currentSource.kind === 'MAINTENANCE' ? 'MAINTENANCE' : 'UNAVAILABLE',
|
|
nextAvailableAt: getNextAvailableAt(nextAvailableAnchor, sources),
|
|
blockingReason: currentSource.kind,
|
|
};
|
|
}
|
|
//# sourceMappingURL=vehicleAvailabilityService.js.map
|