Hotel Booking System
Problem Statement
Section titled “Problem Statement”Design a hotel booking system that allows customers to search for available rooms, make reservations, manage bookings, and handle payments. The system should support multiple hotels, room types, booking cancellations, and pricing strategies.
Requirements
Section titled “Requirements”Functional Requirements
Section titled “Functional Requirements”- Search hotels by location, dates, and room type
- Check room availability for given dates
- Create, modify, and cancel bookings
- Support multiple room types (Single, Double, Suite)
- Handle payment processing
- Generate booking confirmations
- Manage customer profiles
- Support different pricing strategies (seasonal, weekend, holiday)
- Handle overbooking scenarios
- Send booking notifications
Non-Functional Requirements
Section titled “Non-Functional Requirements”- Handle concurrent bookings without double-booking
- High availability for search operations
- ACID properties for booking transactions
- Scalable to handle multiple hotels
- Fast search response time
Simplified Class Diagram
Section titled “Simplified Class Diagram”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startuml
skinparam classBorderThickness 3skinparam ArrowThickness 1skinparam defaultFontSize 16skinparam classAttributeFontSize 18skinparam classFontSize 16
class BookingService { + searchHotels() + checkAvailability() + createBooking() + cancelBooking()}
class Hotel { + getRooms() + getLocation()}
class Room { + checkAvailability() + getRoomType()}
class Booking { + confirm() + cancel() + calculateTotal()}
class Guest { + getProfile() + getBookingHistory()}
class PricingStrategy { + calculatePrice() + applyDiscount()}
class PaymentProcessor { + processPayment() + refund()}
BookingService *-- PricingStrategyBookingService *-- PaymentProcessorBookingService ..> BookingBooking --> GuestBooking --> RoomHotel o-- Room
@endumlSimplified Overview
Section titled “Simplified Overview”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startumlskinparam componentStyle rectangle
package "Booking Service" { [BookingService] as Service}
package "Hotel Management" { interface "IHotel" as Hotel interface "IRoomManager" as RoomMgr interface "IRoom" as Room}
package "Customer & Booking" { interface "IGuest" as Guest interface "IBooking" as Booking}
package "Strategies" { interface "IPricingStrategy" as Pricing interface "IPaymentProcessor" as Payment interface "IInventoryChecker" as Inventory}
package "Notification" { interface "INotificationService" as NotificationIntf}
[HotelBookingDriver] --> Service : usesService *-- Pricing : composed ofService *-- Payment : composed ofService *-- Inventory : composed ofService *-- NotificationIntf : composed ofService o-- Hotel : managesService o-- Booking : managesHotel *-- RoomMgr : composed ofRoomMgr o-- Room : managesBooking o-- GuestBooking o-- RoomBooking o-- Pricing : uses
@endumlDetailed Class Diagram
Section titled “Detailed Class Diagram”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startuml
enum RoomType { SINGLE DOUBLE SUITE DELUXE}
enum BookingStatus { PENDING CONFIRMED CHECKED_IN CHECKED_OUT CANCELLED NO_SHOW}
enum PaymentStatus { PENDING COMPLETED FAILED REFUNDED}
class Address { - street: String - city: String - state: String - zipCode: String - country: String + Address(street: String, city: String, state: String, zipCode: String, country: String) + getCity(): String}
interface IRoom { + getRoomId(): String + getRoomNumber(): String + getType(): RoomType + getPrice(): double + isAvailable(checkIn: Date, checkOut: Date): boolean}
class Room { - roomId: String - roomNumber: String - type: RoomType - price: double - maxOccupancy: int - amenities: List<String> + Room(roomId: String, roomNumber: String, type: RoomType) + getRoomId(): String + getType(): RoomType + getPrice(): double}
interface IHotel { + getHotelId(): String + getName(): String + getAddress(): Address + getRooms(): List<IRoom>}
class Hotel { - hotelId: String - name: String - address: Address - roomManager: IRoomManager - rating: double + Hotel(hotelId: String, name: String, address: Address, manager: IRoomManager) + getHotelId(): String + getName(): String + getAddress(): Address + getRooms(): List<IRoom>}
interface IRoomManager { + addRoom(room: IRoom): void + removeRoom(roomId: String): void + getRooms(): List<IRoom> + getAvailableRooms(checkIn: Date, checkOut: Date, type: RoomType): List<IRoom>}
class RoomManager { - rooms: Map<String, IRoom> - inventoryChecker: IInventoryChecker + RoomManager(checker: IInventoryChecker) + addRoom(room: IRoom): void + getRooms(): List<IRoom> + getAvailableRooms(checkIn: Date, checkOut: Date, type: RoomType): List<IRoom>}
interface IGuest { + getGuestId(): String + getName(): String + getEmail(): String}
class Guest { - guestId: String - name: String - email: String - phone: String - address: Address + Guest(guestId: String, name: String) + getGuestId(): String + getName(): String + getEmail(): String}
interface IBooking { + getBookingId(): String + getGuest(): IGuest + getRoom(): IRoom + getStatus(): BookingStatus + getTotalAmount(): double}
class Booking { - bookingId: String - guest: IGuest - room: IRoom - checkInDate: Date - checkOutDate: Date - numberOfGuests: int - status: BookingStatus - totalAmount: double - pricingCalculator: IPricingStrategy + Booking(guest: IGuest, room: IRoom, checkIn: Date, checkOut: Date, calculator: IPricingStrategy) + getStatus(): BookingStatus + getTotalAmount(): double + calculateTotalAmount(): double}
interface IPricingStrategy { + calculatePrice(room: IRoom, checkIn: Date, checkOut: Date): double}
class StandardPricing { + calculatePrice(room: IRoom, checkIn: Date, checkOut: Date): double}
class SeasonalPricing { - seasonRates: Map<String, Double> + calculatePrice(room: IRoom, checkIn: Date, checkOut: Date): double}
class WeekendPricing { - weekendMultiplier: double + calculatePrice(room: IRoom, checkIn: Date, checkOut: Date): double}
interface IPaymentProcessor { + processPayment(amount: double, method: String): boolean + refund(transactionId: String, amount: double): boolean}
class Payment { - paymentId: String - booking: IBooking - amount: double - status: PaymentStatus - processor: IPaymentProcessor + Payment(booking: IBooking, amount: double, processor: IPaymentProcessor) + processPayment(): boolean + refund(): boolean}
class PaymentProcessor { + processPayment(amount: double, method: String): boolean + refund(transactionId: String, amount: double): boolean}
interface IInventoryChecker { + checkAvailability(room: IRoom, checkIn: Date, checkOut: Date): boolean + blockRoom(room: IRoom, checkIn: Date, checkOut: Date): boolean + releaseRoom(booking: IBooking): void}
class RoomInventory { - bookings: List<IBooking> + checkAvailability(room: IRoom, checkIn: Date, checkOut: Date): boolean + blockRoom(room: IRoom, checkIn: Date, checkOut: Date): boolean + releaseRoom(booking: IBooking): void}
class SearchCriteria { - location: String - checkInDate: Date - checkOutDate: Date - roomType: RoomType - numberOfGuests: int - minPrice: double - maxPrice: double + SearchCriteria(location: String, checkIn: Date, checkOut: Date)}
interface IBookingService { + searchHotels(criteria: SearchCriteria): List<IHotel> + createBooking(guest: IGuest, room: IRoom, checkIn: Date, checkOut: Date): IBooking + cancelBooking(bookingId: String): boolean}
class BookingService { - hotels: Map<String, IHotel> - bookings: Map<String, IBooking> - inventoryChecker: IInventoryChecker - pricingStrategy: IPricingStrategy - paymentProcessor: IPaymentProcessor - notificationService: INotificationService + BookingService(inventory: IInventoryChecker, pricing: IPricingStrategy, payment: IPaymentProcessor, notification: INotificationService) + searchHotels(criteria: SearchCriteria): List<IHotel> + createBooking(guest: IGuest, room: IRoom, checkIn: Date, checkOut: Date): IBooking + cancelBooking(bookingId: String): boolean}
interface INotificationService { + sendBookingConfirmation(booking: IBooking): void + sendCancellationNotification(booking: IBooking): void + sendReminder(booking: IBooking): void}
class EmailNotificationService { + sendBookingConfirmation(booking: IBooking): void + sendCancellationNotification(booking: IBooking): void + sendReminder(booking: IBooking): void}
class HotelBookingDriver { + {static} main(args: String[]): void - setupBookingSystem(): IBookingService - demonstrateBooking(): void}
IRoom <|.. RoomRoom *-- RoomType
IHotel <|.. HotelHotel o-- AddressHotel *-- IRoomManager : composed of
IRoomManager <|.. RoomManagerRoomManager o-- IRoom : managesRoomManager o-- IInventoryChecker : uses
IGuest <|.. GuestGuest o-- Address
IBooking <|.. BookingBooking o-- IGuestBooking o-- IRoomBooking *-- BookingStatusBooking o-- IPricingStrategy : uses
IPricingStrategy <|.. StandardPricingIPricingStrategy <|.. SeasonalPricingIPricingStrategy <|.. WeekendPricing
IPaymentProcessor <|.. PaymentProcessor
Payment o-- IBookingPayment *-- PaymentStatusPayment *-- IPaymentProcessor : composed of
IInventoryChecker <|.. RoomInventoryRoomInventory o-- IBooking : tracks
IBookingService <|.. BookingServiceBookingService o-- IHotel : managesBookingService o-- IBooking : managesBookingService *-- IInventoryChecker : composed ofBookingService *-- IPricingStrategy : composed ofBookingService *-- IPaymentProcessor : composed ofBookingService *-- INotificationService : composed ofBookingService ..> SearchCriteria : uses
INotificationService <|.. EmailNotificationService
HotelBookingDriver ..> IBookingService : usesHotelBookingDriver ..> IGuest : createsHotelBookingDriver ..> IHotel : creates
@endumlKey Design Patterns
Section titled “Key Design Patterns”- Strategy Pattern: Different pricing strategies
- Factory Pattern: Create bookings and rooms
- Observer Pattern: Notify users about booking status
- Singleton Pattern: BookingService as central coordinator
- Repository Pattern: Data access for hotels and bookings
Design Pattern Diagrams
Section titled “Design Pattern Diagrams”1. Strategy Pattern - Pricing Strategies
Section titled “1. Strategy Pattern - Pricing Strategies”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startuml
title Strategy Pattern - Dynamic Pricing
interface IPricingStrategy { + calculatePrice(room, checkIn, checkOut): double}
class StandardPricing { + calculatePrice(room, checkIn, checkOut): double}
class SeasonalPricing { - peakSeasonMultiplier: double - peakSeasonMonths: List<Integer> + calculatePrice(room, checkIn, checkOut): double}
class WeekendPricing { - weekendRate: double - weekdayRate: double + calculatePrice(room, checkIn, checkOut): double}
class DynamicPricing { - occupancyThresholds: Map<Double, Double> + calculatePrice(room, checkIn, checkOut): double}
class BookingService { - pricingStrategy: IPricingStrategy + setPricingStrategy(IPricingStrategy): void + calculateBookingPrice(booking): double}
IPricingStrategy <|.. StandardPricingIPricingStrategy <|.. SeasonalPricingIPricingStrategy <|.. WeekendPricingIPricingStrategy <|.. DynamicPricingBookingService *-- IPricingStrategy
note bottom of StandardPricing Fixed price per night price = basePrice * nightsend note
note bottom of SeasonalPricing Higher rates during peak season if (isPeakSeason) { price *= multiplier }end note
note bottom of WeekendPricing Different rates for weekends vs weekdaysend note
note bottom of DynamicPricing Price based on occupancy: >90% = 1.5x base 70-90% = 1.2x base <70% = 1.0x baseend note
note right of BookingService **Code Example:**
BookingService service = new BookingService();
// Standard pricing for budget hotels service.setPricingStrategy(new StandardPricing()); double price1 = service.calculateBookingPrice(booking); // Result: $100 * 3 nights = $300
// Seasonal pricing for resort service.setPricingStrategy(new SeasonalPricing(2.0)); double price2 = service.calculateBookingPrice(booking); // Result: $100 * 3 nights * 2.0 = $600 (peak season)
// Dynamic pricing based on demand service.setPricingStrategy(new DynamicPricing()); double price3 = service.calculateBookingPrice(booking); // Result: Varies by current occupancyend note
@enduml2. Observer Pattern - Booking Notifications
Section titled “2. Observer Pattern - Booking Notifications”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startuml
title Observer Pattern - Booking Status Notifications
interface IBookingObserver { + onBookingCreated(booking): void + onBookingConfirmed(booking): void + onBookingCancelled(booking): void + onBookingModified(booking): void}
class Booking { - observers: List<IBookingObserver> - status: BookingStatus + addObserver(IBookingObserver): void + removeObserver(IBookingObserver): void + confirm(): void + cancel(): void - notifyObservers(event): void}
class EmailNotificationService { + onBookingCreated(booking): void + onBookingConfirmed(booking): void + onBookingCancelled(booking): void - sendEmail(guest, subject, body): void}
class SMSNotificationService { + onBookingCreated(booking): void + onBookingConfirmed(booking): void - sendSMS(phoneNumber, message): void}
class AnalyticsService { + onBookingCreated(booking): void + onBookingCancelled(booking): void - trackEvent(eventName, data): void}
class InventoryManager { + onBookingConfirmed(booking): void + onBookingCancelled(booking): void - updateAvailability(room, dates): void}
Booking o-- "*" IBookingObserverIBookingObserver <|.. EmailNotificationServiceIBookingObserver <|.. SMSNotificationServiceIBookingObserver <|.. AnalyticsServiceIBookingObserver <|.. InventoryManager
note bottom of Booking **Code Example:**
Booking booking = new Booking(guest, room, dates);
// Register observers booking.addObserver(new EmailNotificationService()); booking.addObserver(new SMSNotificationService()); booking.addObserver(new AnalyticsService()); booking.addObserver(new InventoryManager());
// Single action triggers all observers booking.confirm();
// Results: // - Email sent: "Booking confirmed for Room 305..." // - SMS sent: "Your booking is confirmed" // - Analytics: Track conversion event // - Inventory: Mark room as unavailable for dates
booking.cancel(); // All observers notified of cancellationend note
@enduml3. Factory Pattern - Room & Booking Creation
Section titled “3. Factory Pattern - Room & Booking Creation”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startuml
title Factory Pattern - Room and Booking Factory
class RoomFactory { + {static} createRoom(type, number, hotel): IRoom + {static} createStandardRoom(number, hotel): StandardRoom + {static} createDeluxeRoom(number, hotel): DeluxeRoom + {static} createSuiteRoom(number, hotel): SuiteRoom}
class BookingFactory { + {static} createBooking(guest, room, dates): IBooking + {static} createOnlineBooking(...): OnlineBooking + {static} createWalkInBooking(...): WalkInBooking + {static} createGroupBooking(...): GroupBooking}
interface IRoom { + getRoomType(): RoomType + getBasePrice(): double}
class StandardRoom { - roomNumber: String - basePrice: double = 100 + getRoomType(): RoomType}
class DeluxeRoom { - roomNumber: String - basePrice: double = 200 - hasSeaView: boolean + getRoomType(): RoomType}
class SuiteRoom { - roomNumber: String - basePrice: double = 500 - numberOfRooms: int + getRoomType(): RoomType}
RoomFactory ..> IRoom : createsIRoom <|.. StandardRoomIRoom <|.. DeluxeRoomIRoom <|.. SuiteRoom
note right of RoomFactory **Code Example:**
Hotel hotel = new Hotel("Grand Plaza");
// Create different room types IRoom standard = RoomFactory.createRoom( RoomType.STANDARD, "101", hotel );
IRoom deluxe = RoomFactory.createRoom( RoomType.DELUXE, "501", hotel );
IRoom suite = RoomFactory.createRoom( RoomType.SUITE, "1001", hotel );
// Factory handles complex initialization // - Sets default amenities // - Configures pricing // - Links to hotel // - Registers in room inventoryend note
note right of BookingFactory **Code Example:**
// Different booking types IBooking online = BookingFactory.createOnlineBooking( guest, room, checkIn, checkOut, paymentInfo );
IBooking walkIn = BookingFactory.createWalkInBooking( guest, room, checkIn, checkOut );
IBooking group = BookingFactory.createGroupBooking( guests, rooms, dates, discountCode );
// Each type has different: // - Cancellation policies // - Payment requirements // - Discount applicabilityend note
@enduml4. Repository Pattern - Data Access Layer
Section titled “4. Repository Pattern - Data Access Layer”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startuml
interface BookingRepository { + save(booking: Booking): void + findById(id: String): Booking + findByGuest(guestId: String): List<Booking> + findByRoom(roomId: String, start: Date, end: Date): List<Booking> + update(booking: Booking): void + delete(id: String): void}
interface RoomRepository { + save(room: Room): void + findById(id: String): Room + findByType(type: RoomType): List<Room> + findAvailable(start: Date, end: Date): List<Room> + update(room: Room): void}
class InMemoryBookingRepository { - bookings: Map<String, Booking> + save(booking: Booking): void + findById(id: String): Booking + findByGuest(guestId: String): List<Booking> + findByRoom(roomId: String, start: Date, end: Date): List<Booking> + update(booking: Booking): void + delete(id: String): void}
class DatabaseBookingRepository { - connection: Connection - queryBuilder: QueryBuilder + save(booking: Booking): void + findById(id: String): Booking + findByGuest(guestId: String): List<Booking> + findByRoom(roomId: String, start: Date, end: Date): List<Booking> + update(booking: Booking): void + delete(id: String): void}
class BookingService { - bookingRepository: BookingRepository - roomRepository: RoomRepository + createBooking(bookingData): Booking + cancelBooking(bookingId: String): void + findAvailableRooms(start: Date, end: Date): List<Room>}
BookingRepository <|.. InMemoryBookingRepositoryBookingRepository <|.. DatabaseBookingRepositoryBookingService o-- BookingRepositoryBookingService o-- RoomRepository
note right of BookingRepository Repository Pattern Benefits:
1. **Abstraction**: Business logic doesn't depend on storage implementation
2. **Flexibility**: Easy to switch between in-memory, database, or external APIs
3. **Testability**: Can use in-memory repositories for testing
4. **Centralized Queries**: All data access logic in one place
5. **Caching**: Can add caching layer transparentlyend note
note bottom of InMemoryBookingRepository // In-memory implementation for testing public class InMemoryBookingRepository implements BookingRepository {
private Map<String, Booking> bookings = new ConcurrentHashMap<>();
@Override public void save(Booking booking) { bookings.put(booking.getId(), booking); }
@Override public Booking findById(String id) { return bookings.get(id); }
@Override public List<Booking> findByGuest(String guestId) { return bookings.values().stream() .filter(b -> b.getGuest().getId().equals(guestId)) .collect(Collectors.toList()); }
@Override public List<Booking> findByRoom( String roomId, Date start, Date end) { return bookings.values().stream() .filter(b -> b.getRoom().getId().equals(roomId)) .filter(b -> datesOverlap( b.getCheckInDate(), b.getCheckOutDate(), start, end)) .collect(Collectors.toList()); } }end note
note bottom of DatabaseBookingRepository // Database implementation for production public class DatabaseBookingRepository implements BookingRepository {
private Connection connection;
@Override public void save(Booking booking) { String sql = """ INSERT INTO bookings (id, guest_id, room_id, check_in, check_out, status) VALUES (?, ?, ?, ?, ?, ?) """; // Execute SQL... }
@Override public Booking findById(String id) { String sql = """ SELECT * FROM bookings WHERE id = ? """; // Execute query and map to Booking object }
@Override public List<Booking> findByRoom( String roomId, Date start, Date end) { String sql = """ SELECT * FROM bookings WHERE room_id = ? AND check_out > ? AND check_in < ? AND status != 'CANCELLED' """; // Execute query and map to List<Booking> } }end note
note bottom of BookingService // Service uses repositories without // knowing storage implementation public class BookingService { private BookingRepository bookingRepo; private RoomRepository roomRepo;
// Constructor injection public BookingService( BookingRepository bookingRepo, RoomRepository roomRepo) { this.bookingRepo = bookingRepo; this.roomRepo = roomRepo; }
public Booking createBooking( Guest guest, Room room, Date checkIn, Date checkOut) {
// Check availability using repository List<Booking> conflicts = bookingRepo.findByRoom( room.getId(), checkIn, checkOut);
if (!conflicts.isEmpty()) { throw new RoomNotAvailableException(); }
Booking booking = new Booking( guest, room, checkIn, checkOut); bookingRepo.save(booking); return booking; }
public List<Room> findAvailableRooms( Date checkIn, Date checkOut) { List<Room> allRooms = roomRepo.findAll();
return allRooms.stream() .filter(room -> { List<Booking> bookings = bookingRepo.findByRoom( room.getId(), checkIn, checkOut); return bookings.isEmpty(); }) .collect(Collectors.toList()); } }end note
@endumlCode Snippets
Section titled “Code Snippets”Create Booking
Section titled “Create Booking”public class BookingService { public Booking createBooking(Guest guest, Room room, Date checkIn, Date checkOut) throws BookingException { synchronized(this) { // Check availability RoomInventory inventory = inventoryMap.get(room.getHotel().getHotelId()); if (!inventory.checkAvailability(room, checkIn, checkOut)) { throw new BookingException("Room not available for selected dates"); }
// Create booking Booking booking = new Booking(guest, room, checkIn, checkOut);
// Calculate price double totalAmount = pricingStrategy.calculatePrice(room, checkIn, checkOut); booking.setTotalAmount(totalAmount);
// Block room if (!inventory.blockRoom(room, checkIn, checkOut)) { throw new BookingException("Failed to block room"); }
// Save booking booking.setStatus(BookingStatus.PENDING); bookings.put(booking.getBookingId(), booking);
// Send notification notificationService.sendBookingConfirmation(booking);
return booking; } }}Check Room Availability
Section titled “Check Room Availability”public class RoomInventory { public boolean checkAvailability(Room room, Date checkIn, Date checkOut) { for (Booking booking : bookings) { if (!booking.getRoom().equals(room)) { continue; }
if (booking.getStatus() == BookingStatus.CANCELLED) { continue; }
// Check for overlap if (datesOverlap(booking.getCheckInDate(), booking.getCheckOutDate(), checkIn, checkOut)) { return false; } } return true; }
private boolean datesOverlap(Date start1, Date end1, Date start2, Date end2) { return !(end1.before(start2) || end2.before(start1)); }}Seasonal Pricing Strategy
Section titled “Seasonal Pricing Strategy”public class SeasonalPricing implements PricingStrategy { private Map<Season, Double> seasonRates;
@Override public double calculatePrice(Room room, Date checkIn, Date checkOut) { double basePrice = room.getPrice(); long nights = calculateNights(checkIn, checkOut);
double totalPrice = 0; Calendar cal = Calendar.getInstance(); cal.setTime(checkIn);
for (int i = 0; i < nights; i++) { Season season = getSeason(cal.getTime()); double multiplier = seasonRates.getOrDefault(season, 1.0); totalPrice += basePrice * multiplier; cal.add(Calendar.DAY_OF_MONTH, 1); }
return totalPrice; }
private Season getSeason(Date date) { Calendar cal = Calendar.getInstance(); cal.setTime(date); int month = cal.get(Calendar.MONTH);
if (month >= 11 || month <= 1) return Season.WINTER; if (month >= 2 && month <= 4) return Season.SPRING; if (month >= 5 && month <= 7) return Season.SUMMER; return Season.FALL; }}Search Hotels
Section titled “Search Hotels”public class BookingService { public List<Hotel> searchHotels(SearchCriteria criteria) { List<Hotel> results = new ArrayList<>();
for (Hotel hotel : hotels.values()) { if (!matchesLocation(hotel, criteria.getLocation())) { continue; }
List<Room> availableRooms = hotel.getAvailableRooms( criteria.getCheckInDate(), criteria.getCheckOutDate(), criteria.getRoomType() );
if (!availableRooms.isEmpty()) { results.add(hotel); } }
return results; }}Extension Points
Section titled “Extension Points”- Add loyalty programs and rewards
- Implement dynamic pricing based on demand
- Add room upgrade options
- Support group bookings
- Implement waitlist for fully booked hotels
- Add review and rating system
- Support multi-room bookings
- Implement booking modifications with price adjustments
- Add integration with payment gateways
- Support corporate accounts and bulk bookings