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>| Parameter | Type | Description |
|---|---|---|
roomId | string | Room ID to check |
startsAt | Date | Proposed start time |
endsAt | Date | Proposed end time |
excludeBookingId | string | Optional 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[]>| Parameter | Type | Description |
|---|---|---|
roomId | string | Originally requested room ID |
startsAt | Date | Originally requested start time |
endsAt | Date | Originally requested end time |
filter | AvailabilityFilter | Optional 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| Parameter | Type | Description |
|---|---|---|
existingBooking | Booking | The already-persisted booking |
newBooking | Booking | The 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>| Parameter | Type | Description |
|---|---|---|
data | CreateConflictRecordDto | Resolution 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;
}