My App
RoomKitBackend

ConflictService

Detect booking conflicts and suggest alternatives

Overview

ConflictService checks for booking overlaps including partition tree conflicts, suggests alternative times and rooms, and resolves conflicts based on priority tiers.

import { ConflictService } from '@roomkit/nestjs';

Methods

checkConflicts

Check whether a proposed time range conflicts with existing bookings for a room. Checks both direct overlaps and partition tree overlaps (e.g., booking a parent room when a child partition is booked).

async checkConflicts(
    roomId: string,
    startsAt: Date,
    endsAt: Date,
    excludeBookingId?: string,
): Promise<ConflictCheckResult>
ParameterTypeDescription
roomIdstringRoom ID to check
startsAtDateProposed start time
endsAtDateProposed end time
excludeBookingIdstringOptional booking ID to exclude (useful when modifying an existing booking)
const result = await conflictService.checkConflicts(
    'room-a1',
    new Date('2026-03-02T09:00:00'),
    new Date('2026-03-02T10:00:00'),
);

if (result.hasConflicts) {
    console.log('Conflicting bookings:', result.conflicts);
}

suggestAlternatives

Suggest alternative booking options when a conflict is detected. Returns two types of suggestions: same room at different times, and different rooms at the same time.

async suggestAlternatives(
    roomId: string,
    startsAt: Date,
    endsAt: Date,
    filter?: AvailabilityFilter,
): Promise<AlternativeSuggestion[]>
ParameterTypeDescription
roomIdstringOriginally requested room ID
startsAtDateOriginally requested start time
endsAtDateOriginally requested end time
filterAvailabilityFilterOptional filter to constrain alternative room suggestions

Same-room-different-time suggestions scan forward in 30-minute increments over a 7-day window, returning up to 3 options.

Different-room-same-time suggestions find available rooms at the same time, scored by capacity similarity (score range 0.5 to 0.9).

const alternatives = await conflictService.suggestAlternatives(
    'room-a1',
    new Date('2026-03-02T09:00:00'),
    new Date('2026-03-02T10:00:00'),
    { minCapacity: 20, requiredEquipment: ['projector'] },
);

for (const alt of alternatives) {
    console.log(alt.type, alt.roomId, alt.startsAt, alt.score);
}

resolveByPriority

Determine which booking wins a conflict based on priority tiers. The higher priority booking wins. In the case of a tie, the existing booking takes precedence.

resolveByPriority(
    existingBooking: Booking,
    newBooking: Booking,
): ConflictResolution
ParameterTypeDescription
existingBookingBookingThe already-persisted booking
newBookingBookingThe incoming booking requesting the same slot

Returns a ConflictResolution indicating which booking wins and which should be displaced.

const resolution = conflictService.resolveByPriority(existingBooking, newBooking);

if (resolution.winner.id === newBooking.id) {
    // New booking has higher priority — displace the existing one
    await bookingService.cancel(existingBooking.id, 'system', 'Displaced by higher priority');
}

recordResolution

Persist a conflict resolution record for audit purposes.

async recordResolution(data: CreateConflictRecordDto): Promise<ConflictRecord>
ParameterTypeDescription
dataCreateConflictRecordDtoResolution data to persist
const record = await conflictService.recordResolution({
    bookingId: newBooking.id,
    conflictingBookingId: existingBooking.id,
    resolution: 'displaced',
    resolvedBy: 'system',
    reason: 'Higher priority tier',
});

ConflictCheckResult

interface ConflictCheckResult {
    hasConflicts: boolean;
    conflicts: Booking[];
}

AlternativeSuggestion

interface AlternativeSuggestion {
    type: 'same-room-different-time' | 'different-room-same-time';
    roomId: string;
    startsAt: Date;
    endsAt: Date;
    score: number;
}

On this page