Skip to main content

EP-003: Reservation Management

Business Objective​

Reservations are the primary revenue pipeline for any hotel. Every confirmed booking represents committed revenue; every lost or mismanaged booking represents direct financial loss. This epic covers the complete reservation lifecycle from initial booking through check-out, supporting 13 booking sources (OTA, direct, walk-in, corporate), multi-room stays, group bookings with allotments, waitlist management, and real-time availability calculations. In the Indian hospitality market, hotels must handle walk-ins (30-40% of bookings for budget/mid-segment), OTA parity requirements, and GST-compliant invoicing. The system must process 500+ concurrent availability checks during peak booking windows without degradation.

Audience​

  • Primary: Reservations Agent, Front Desk Agent (create/modify/cancel bookings)
  • Secondary: Manager (group bookings, overbooking decisions), Revenue Manager (availability controls, restrictions)
  • Tertiary: Guest (via booking engine), OTA Channels (via channel manager)

Scope​

In Scope​

  • Reservation CRUD with 6 statuses: CONFIRMED, CHECKED_IN, CHECKED_OUT, CANCELLED, NO_SHOW, WAITLISTED (plus PENDING for payment-pending bookings)
  • 13 booking sources: DIRECT, BOOKING_COM, EXPEDIA, AGODA, MAKEMYTRIP, GOIBIBO, AIRBNB, GDS, CORPORATE, TRAVEL_AGENT, WALKIN, BOOKING_ENGINE, OTHER
  • Multi-room reservations (one guest, multiple room types/rooms across dates)
  • Confirmation number generation (unique, property-prefixed)
  • Guest lookup and linking during reservation creation
  • Arrival and departure date management with length-of-stay calculation
  • Room assignment (manual or auto-suggest based on preferences)
  • Reservation search with filters (date range, status, source, guest name, confirmation number)
  • Arrivals board, departures board, in-house guest list
  • Group bookings with allotment blocks, rooming lists, cutoff dates
  • Waitlist management with offer/accept workflow
  • Real-time availability engine (inventory check across date range per room type)
  • Inventory holds (temporary lock during booking flow with TTL expiry)
  • Availability calendar (daily inventory breakdown per room type)
  • Reservation modification tracking (ReservationHistory with changes JSON)
  • Cancellation with reason tracking and policy-based charge calculation
  • No-show processing
  • Payment initiation, verification, webhook handling, refunds (Razorpay integration)
  • Payment link generation for pre-arrival payment collection
  • Routing rules for charge routing (room, F&B, tax to different folios)
  • Overbooking controls

Out of Scope​

  • Booking engine UI (covered in EP-010)
  • Channel manager sync (covered in EP-009)
  • Folio management (covered in EP-005 -- Front Desk Operations)
  • Dynamic pricing / yield management algorithms (covered in EP-012)

Current Implementation Status​

  • Backend (reservation-service): Fully built. ReservationsController with POST /reservations (5 roles), GET /properties/:propertyId/reservations, GET /reservations/:id, GET /reservations/confirmation/:confirmationNumber, PUT /reservations/:id, DELETE /reservations/:id (cancel with CancelReservationDto), POST /reservations/search with SearchReservationsDto, POST /reservations/assign-room with AssignRoomDto, POST /reservations/:id/no-show. Arrivals (GET /properties/:propertyId/arrivals?date=), departures, in-house list. @Audit decorator on create/update/cancel/assign/no-show.
  • Backend (availability): AvailabilityController -- POST /availability/check (public), GET /availability (public, query params), POST /availability/hold, GET /availability/hold/:holdId, DELETE /availability/hold/:holdId (204), GET /inventory/:propertyId/:roomTypeId?startDate=&endDate= (inventory calendar).
  • Backend (groups): GroupsController -- POST /groups, GET /properties/:propertyId/groups, GET /groups/:id, PUT /groups/:id, DELETE /groups/:id (cancel with reason), POST /groups/:id/confirm, POST /groups/:id/reservations (add rooms), GET /groups/:id/rooming (rooming list). Group model with type (GROUP, ALLOTMENT, BLOCK), status (TENTATIVE, CONFIRMED, CANCELLED), cutoff date, rooms blocked/confirmed/picked-up.
  • Backend (waitlist): WaitlistController -- POST /waitlist, GET /waitlist (filter by property, roomType, status), DELETE /waitlist/:id, POST /waitlist/:id/accept. Statuses: WAITING, OFFERED, ACCEPTED, EXPIRED, CANCELLED.
  • Backend (payments): PaymentsController -- POST /payments/initiate, POST /payments/verify, POST /payments/webhook (Razorpay signature verification), POST /payments/refund, GET /payments/:id/status.
  • Backend (routing): RoutingRulesController exists for charge routing.
  • Dashboard: Reservations list page with filters. Calendar view. Groups page. Reservation form with guest lookup, date picker, room type selector, rate plan selector, source dropdown. Amend dialog for modifications. Arrivals/departures/in-house tabs in Front Desk module.

Gap Analysis​

  • Group Booking UI: Backend is comprehensive (CRUD, confirm, add reservations, rooming list). Dashboard groups page exists but needs polish: allotment block visual (date x room-type grid showing blocked vs. picked-up vs. available), rooming list upload from Excel, group folio summary. Impact: High for MICE (Meetings, Incentives, Conferences, Events) segment hotels.
  • Waitlist Management UI: Backend is complete. Dashboard lacks a dedicated waitlist view with offer/accept workflow visualization. Staff must use API directly. Impact: Medium -- hotels with high demand periods need this.
  • Overbooking Controls: OverbookingController exists in analytics-service but is not configurable per room type from the dashboard. Hotels need to set overbooking % per room type (e.g., allow 5% overbooking on Standard, 0% on Suite). Impact: High for revenue optimization at city hotels with 90%+ occupancy.
  • Cancellation Charge Automation: Cancellation policies exist and are linked to rate plans, but the actual charge calculation at cancellation time is not automated. The agent manually enters the cancellation charge. Impact: Medium -- can lead to inconsistent charge application.
  • Payment Link Delivery: Payment link generation exists via Razorpay integration. However, sending the link via SMS/WhatsApp is not integrated (notification-service exists but not wired to payment flow). Impact: Medium for pre-arrival payment collection.
  • Modification Tracking UI: ReservationHistory model records all changes as JSON. Dashboard lacks a visual modification history timeline on the reservation detail page. Impact: Low -- data exists, UI needed.
  • Bulk Operations: No bulk cancellation, bulk room assignment, or bulk status update for group bookings. Impact: Medium for group check-in scenarios.

User Stories​

Story IDTitlePriorityPointsStatusSprint
US-003-001Create Individual ReservationP05Built2
US-003-002Multi-Room ReservationP05Built2
US-003-003Reservation Search & FiltersP05Built2
US-003-004Reservation ModificationP05Built3
US-003-005Reservation Cancellation with Policy EnforcementP05Built3
US-003-006Real-Time Availability EngineP08Built2
US-003-007Inventory Hold (Temporary Lock)P03Built2
US-003-008Room AssignmentP05Built3
US-003-009Arrivals / Departures / In-House BoardsP05Built3
US-003-010No-Show ProcessingP03Built3
US-003-011Confirmation Number Generation & LookupP02Built2
US-003-012Group Booking CRUDP18Built4
US-003-013Group Allotment & Rooming ListP18Partial5
US-003-014Waitlist ManagementP15Partial5
US-003-015Payment Initiation & Verification (Razorpay)P18Built4
US-003-016Payment Link Generation & DeliveryP15Partial6
US-003-017Reservation Modification HistoryP13Partial5
US-003-018Overbooking Control ConfigurationP25Partial8
US-003-019Cancellation Charge Auto-CalculationP25Not Started8
US-003-020Charge Routing RulesP25Built6
US-003-021Bulk Group Check-In & Room AssignmentP28Not Started10
US-003-022Availability Calendar VisualizationP25Partial8

Dependencies​

  • EP-001 (Auth): All reservation operations require authenticated users with appropriate roles.
  • EP-002 (Property): Reservations depend on room types, rooms, rate plans, and cancellation policies.
  • EP-004 (Guest): Reservations link to guest profiles (guestId FK).
  • EP-005 (Front Desk): Check-in/out transitions are handled by front desk service.
  • EP-009 (Channel Manager): OTA bookings flow through channel service into reservation service.

Acceptance Criteria (Epic Level)​

  • Reservations can be created from all 13 booking sources with complete data capture
  • Multi-room reservations link multiple room types/rooms to a single confirmation number
  • Real-time availability checks complete in < 500ms for 30-day date ranges
  • Inventory holds expire after configurable TTL (default 15 minutes)
  • Group bookings support allotment blocks with cutoff dates and rooming lists
  • Cancellation applies the correct policy-based charges automatically
  • No-show processing marks reservations and applies no-show charges
  • Payment can be collected via Razorpay (UPI, card, net banking)
  • All modifications are tracked in ReservationHistory with before/after JSON
  • Waitlist entries can be offered rooms when availability opens and accepted by guest

KPIs / Success Metrics​

  • Reservation creation success rate: > 99.5%
  • Availability check p95 latency: < 500ms
  • Double-booking rate: 0% (except controlled overbooking)
  • Cancellation policy compliance: 100% (charges match policy rules)
  • Group pickup rate tracking: visible per group with cutoff date alerts
  • Payment collection rate (pre-arrival): > 60% for advance payment rate plans

Detailed User Stories​

US-003-001: Create Individual Reservation​

As a reservations agent, I want to create a reservation by selecting guest, dates, room type, and rate plan, So that the hotel has a confirmed booking with all details captured for arrival preparation.

Priority: P0 | Points: 5 | Sprint: 2 | Status: Built

Implementation Status​

  • Backend: POST /reservations with CreateReservationDto. Accepts guestId, propertyId, arrivalDate, departureDate, adults, children, roomReservations (array of roomTypeId + ratePlanId), source, specialRequests, internalNotes, promoCode. Returns created reservation with confirmationNumber, totalAmount, taxAmount, balanceDue. @Audit logged. Roles: SUPER_ADMIN, ADMIN, MANAGER, RESERVATIONS, FRONT_DESK.
  • Frontend: Reservation form with guest lookup (search-as-you-type), date range picker, room type selector with availability indicator, rate plan dropdown, source selector (13 options), special requests textarea.

Acceptance Criteria​

Scenario: Create a reservation for a returning guest
Given the reservations agent searches for guest "Rajesh Sharma" and finds the profile
When they create a reservation with arrival 2026-04-15, departure 2026-04-18 (3 nights),
room type "Deluxe", rate plan "Rack Rate" at INR 5500/night, source DIRECT,
special requests "High floor, non-smoking"
Then a reservation is created with status CONFIRMED
And a unique confirmation number is generated (e.g., "GPH20260415001")
And totalAmount is INR 16,500 (3 x 5,500) plus applicable GST
And the reservation appears in the arrivals board for April 15

Scenario: Prevent reservation when no availability
Given all Deluxe rooms are sold out for April 20
When the agent attempts to create a reservation for Deluxe on April 20-21
Then the system returns 409 with message "No availability for Deluxe Room on 2026-04-20"

Scenario: Create OTA-sourced reservation
Given a booking is received from Booking.com
When the reservation is created with source BOOKING_COM, sourceReference "BDC-123456"
Then the reservation records the OTA source and reference ID for reconciliation
And the channelId links to the Booking.com channel connection

US-003-002: Multi-Room Reservation​

As a reservations agent, I want to book multiple rooms of different types under a single reservation, So that a family or corporate traveler has one confirmation number for their entire stay.

Priority: P0 | Points: 5 | Sprint: 2 | Status: Built

Implementation Status​

  • Backend: CreateReservationDto accepts roomReservations as an array, each with roomTypeId, ratePlanId, checkInDate, checkOutDate, adults, children. Creates one Reservation with multiple RoomReservation records.
  • Frontend: "Add Room" button on reservation form to add additional room lines.

Acceptance Criteria​

Scenario: Book two rooms for a family trip
Given a guest requests 1 Deluxe and 1 Standard room for April 10-13
When the agent creates a reservation with 2 room reservations:
- Deluxe, Rack Rate, 2 adults, 0 children
- Standard, Rack Rate, 2 adults, 1 child (extra child charge INR 600/night)
Then one reservation is created with one confirmation number
And totalAmount includes: Deluxe (5500 x 3) + Standard (3500 x 3) + extra child (600 x 3) = INR 28,800 plus GST
And both room reservations are linked to the single reservation record

Scenario: Different date ranges for rooms in same reservation
Given a guest arrives April 10 but their colleague joins on April 11
When the agent creates a multi-room reservation:
- Room 1: April 10-13 (3 nights)
- Room 2: April 11-13 (2 nights)
Then the reservation arrival date is April 10 (earliest) and departure is April 13 (latest)
And each room reservation has its own check-in and check-out dates

US-003-003: Reservation Search & Filters​

As a front desk agent, I want to search reservations by confirmation number, guest name, date range, status, or source, So that I can quickly locate any booking in the system.

Priority: P0 | Points: 5 | Sprint: 2 | Status: Built

Implementation Status​

  • Backend: POST /reservations/search with SearchReservationsDto. Supports filters: confirmation number, guest name (fuzzy), arrival/departure date range, status (array), source (array), groupId. Pagination via PaginationDto. Also: GET /reservations/confirmation/:confirmationNumber for exact lookup.
  • Frontend: Reservation list page with search bar and filter sidebar (date range, status checkboxes, source dropdown).

Acceptance Criteria​

Scenario: Search by partial guest name
Given there are 50 reservations, 3 belonging to guests with surname "Sharma"
When the agent searches with guest name "Sharma"
Then 3 reservations are returned matching the surname, ordered by arrival date

Scenario: Filter today's arrivals by OTA source
Given 20 arrivals are expected today from various sources
When the agent filters by date "today" and source ["BOOKING_COM", "MAKEMYTRIP"]
Then only reservations from those two OTA sources are shown
And results include confirmation number, guest name, room type, and source

Scenario: Lookup by confirmation number
Given a guest calls with confirmation number "GPH20260415001"
When the agent calls GET /reservations/confirmation/GPH20260415001
Then the exact reservation is returned with full details including guest, rooms, and payment status

US-003-004: Reservation Modification​

As a reservations agent, I want to modify reservation dates, room type, rate plan, or guest count, So that changes are captured with a full audit trail and the guest's updated requirements are met.

Priority: P0 | Points: 5 | Sprint: 3 | Status: Built

Implementation Status​

  • Backend: PUT /reservations/:id with UpdateReservationDto. Captures @CurrentUser() for audit. @Audit decorator logs changes. ReservationHistory model records action "MODIFIED" with changes JSON (old/new values).
  • Frontend: "Amend" dialog on reservation detail page with editable fields.

Acceptance Criteria​

Scenario: Extend stay by 2 nights
Given reservation GPH20260415001 is for April 15-18 (3 nights) at INR 16,500
When the agent extends departure to April 20 (now 5 nights)
Then the reservation is updated with new departure date
And totalAmount is recalculated to INR 27,500 (5 x 5,500) plus GST
And a ReservationHistory record is created: action "MODIFIED",
changes { "departureDate": { "old": "2026-04-18", "new": "2026-04-20" }, "totalAmount": { "old": 16500, "new": 27500 } }

Scenario: Upgrade room type
Given the guest requests an upgrade from Standard to Deluxe
When the agent changes the room type on the reservation
Then availability is checked for Deluxe on the stay dates
And the rate is recalculated at the Deluxe rate
And the modification is recorded with old and new room type

Scenario: Reject modification that exceeds availability
Given all Suite rooms are sold out for April 16
When the agent attempts to upgrade a reservation to Suite for April 15-18
Then the system returns 409 with message "No availability for Suite on 2026-04-16"
And the reservation remains unchanged

US-003-005: Reservation Cancellation with Policy Enforcement​

As a reservations agent, I want to cancel a reservation and apply the correct cancellation charges per policy, So that the hotel collects appropriate compensation for late cancellations.

Priority: P0 | Points: 5 | Sprint: 3 | Status: Built

Implementation Status​

  • Backend: DELETE /reservations/:id with CancelReservationDto (includes cancellation reason). Sets status to CANCELLED, records cancelledAt, cancelledBy, cancellationReason. @Audit logged. Roles: SUPER_ADMIN, ADMIN, MANAGER, RESERVATIONS.
  • Gap: Cancellation charge is not auto-calculated from the linked cancellation policy. Agent must specify the charge manually.

Acceptance Criteria​

Scenario: Cancel reservation with flexible policy (free cancellation)
Given reservation GPH20260415001 has the "Flexible" cancellation policy
and arrival is 10 days away
When the agent cancels with reason "Guest travel plans changed"
Then the reservation status changes to CANCELLED
And cancelledAt and cancellationReason are recorded
And no cancellation charge is applied (10 days > 7-day free cancellation window)
And the room inventory is released back to availability

Scenario: Late cancellation incurs first-night charge
Given reservation GPH20260420001 has the "Flexible" policy and arrival is 2 days away
When the agent cancels with reason "Personal emergency"
Then the reservation is cancelled
And the system indicates a 100% first-night charge applies (within 0-3 day window)
And the cancellation charge amount is recorded for billing

Scenario: Cancel non-refundable reservation
Given the reservation was booked on a "Non-Refundable" rate plan
When the agent cancels the reservation
Then a warning is shown: "This is a non-refundable booking. Full stay charges apply."
And upon confirmation, the reservation is cancelled with full charge retained

US-003-006: Real-Time Availability Engine​

As a reservations agent or booking engine, I want to check room availability for a given property, date range, and occupancy, So that only available room types and rates are offered to the guest.

Priority: P0 | Points: 8 | Sprint: 2 | Status: Built

Implementation Status​

  • Backend: AvailabilityController -- two availability check endpoints (POST and GET, both @Public()). POST /availability/check accepts CheckAvailabilityDto (propertyId, checkInDate, checkOutDate, adults, children, optional roomTypeId, ratePlanId, promoCode). GET /availability with query params (same fields). Returns available room types with rates, room counts, and applicable restrictions.
  • Inventory Calendar: GET /inventory/:propertyId/:roomTypeId?startDate=&endDate= returns daily breakdown of total rooms, sold rooms, blocked rooms, available rooms.

Acceptance Criteria​

Scenario: Check availability for a 3-night stay
Given the property has 20 Deluxe rooms and 15 are booked for April 15-18
When availability is checked for April 15-18, 2 adults
Then the response includes Deluxe Room with 5 available rooms
And the rate for the Rack Rate plan is returned (INR 5500/night or date-level rate if set)
And room types with zero availability are excluded from results

Scenario: Availability check respects restrictions
Given the Rack Rate has minLOS 3 for April 20-26 (Diwali week)
When availability is checked for April 20-21 (1 night only)
Then the Rack Rate plan is not returned for that search
And other rate plans without the restriction may still appear

Scenario: Promo code applies discounted rate
Given a promo code "SUMMER20" offers 20% off the Rack Rate
When availability is checked with promoCode "SUMMER20"
Then the response includes discounted rates for applicable room types
And the original rate and discount amount are shown for transparency

US-003-007: Inventory Hold (Temporary Lock)​

As a booking engine, I want to place a temporary hold on inventory while the guest completes payment, So that the room is not double-booked during the checkout flow.

Priority: P0 | Points: 3 | Sprint: 2 | Status: Built

Implementation Status​

  • Backend: POST /availability/hold with CreateHoldDto -- creates a temporary inventory hold with TTL. GET /availability/hold/:holdId checks hold status. DELETE /availability/hold/:holdId releases the hold. Returns holdId and expiry timestamp.

Acceptance Criteria​

Scenario: Create and release an inventory hold
Given 3 Deluxe rooms are available for April 15-18
When the booking engine creates a hold for 1 Deluxe room
Then a holdId is returned with a 15-minute expiry
And availability now shows 2 Deluxe rooms available
And if the hold is released (DELETE), availability returns to 3

Scenario: Hold expires automatically after TTL
Given a hold was created 16 minutes ago with 15-minute TTL
When the booking engine checks hold status via GET /availability/hold/:holdId
Then the hold status is "expired"
And the inventory is automatically released

US-003-008: Room Assignment​

As a front desk agent, I want to assign a specific room to a reservation based on guest preferences, So that the guest gets a room that matches their requests (high floor, non-smoking, connecting).

Priority: P0 | Points: 5 | Sprint: 3 | Status: Built

Implementation Status​

  • Backend: POST /reservations/assign-room with AssignRoomDto (roomReservationId, roomId). Links a specific Room to a RoomReservation record. @Audit logged.
  • Frontend: Room assignment dialog with available rooms list filtered by room type, showing room features.

Acceptance Criteria​

Scenario: Assign a high-floor non-smoking room
Given a reservation requests "High floor, non-smoking" and the room type is Deluxe
When the front desk agent views available Deluxe rooms filtered by isSmoking=false
Then rooms on floors 4 and 5 are shown with their features
And the agent assigns room 502 (5th floor, non-smoking, city-view)
And the room reservation record is updated with roomId for room 502

Scenario: Prevent double-assignment of a room
Given room 502 is already assigned to another reservation for overlapping dates
When the agent attempts to assign room 502 to a second reservation
Then the system returns 409 with message "Room 502 is already assigned for these dates"

US-003-009: Arrivals / Departures / In-House Boards​

As a front desk agent, I want to view today's arrivals, departures, and in-house guests, So that I can prepare for incoming guests, process checkouts, and manage current occupancy.

Priority: P0 | Points: 5 | Sprint: 3 | Status: Built

Implementation Status​

  • Backend: GET /properties/:propertyId/arrivals?date= (both reservation-service and frontdesk-service have this endpoint), GET /properties/:propertyId/departures?date=, GET /properties/:propertyId/in-house. Returns reservation details with guest info, room assignments, and special requests.
  • Frontend: Three tabs in Front Desk module: Arrivals (today's expected check-ins), Departures (today's expected check-outs), In-House (currently checked-in guests).

Acceptance Criteria​

Scenario: View today's arrivals board
Given 12 guests are expected to arrive today
When the front desk agent opens the Arrivals tab
Then 12 reservations are listed with: guest name, confirmation number, room type,
assigned room (if any), arrival time (if noted), special requests, VIP level
And reservations without room assignment are highlighted for action

Scenario: View departures with outstanding balances
Given 8 guests are expected to depart today
When the agent views the Departures tab
Then each departure shows: guest name, room number, balance due, checkout time
And guests with outstanding balance > 0 are flagged in red

Scenario: In-house list shows current occupancy
Given 65 rooms are currently occupied
When the agent views the In-House tab
Then 65 entries are shown with: guest name, room number, check-in date,
expected departure, nights remaining, and folio balance

US-003-010: No-Show Processing​

As a front desk agent during night audit, I want to mark reservations as no-show when guests don't arrive by the cutoff time, So that rooms are released and no-show charges are applied per policy.

Priority: P0 | Points: 3 | Sprint: 3 | Status: Built

Implementation Status​

  • Backend: POST /reservations/:id/no-show. Sets status to NO_SHOW, records noShowAt timestamp. @Audit logged. Roles: SUPER_ADMIN, ADMIN, MANAGER, FRONT_DESK.

Acceptance Criteria​

Scenario: Process no-show for a confirmed reservation
Given reservation GPH20260415005 is CONFIRMED but the guest has not arrived by midnight
When the night auditor marks the reservation as no-show
Then the reservation status changes to NO_SHOW
And noShowAt timestamp is recorded
And the assigned room (if any) is released
And a no-show charge (typically first night) is applied to the folio

Scenario: Cannot mark an already checked-in reservation as no-show
Given reservation GPH20260415003 has status CHECKED_IN
When the agent attempts to mark it as no-show
Then the system returns 409 with message "Reservation is already checked in"

US-003-011: Confirmation Number Generation & Lookup​

As a guest calling the hotel, I want to look up my reservation using the confirmation number, So that the front desk can quickly find my booking without asking for other details.

Priority: P0 | Points: 2 | Sprint: 2 | Status: Built

Implementation Status​

  • Backend: Confirmation number is auto-generated and stored as unique field. GET /reservations/confirmation/:confirmationNumber for direct lookup.

Acceptance Criteria​

Scenario: Unique confirmation number generation
Given the property code is "GPH" and the date is April 15, 2026
When a reservation is created
Then a confirmation number is generated in format "GPH{YYYYMMDD}{SEQ}" (e.g., "GPH20260415001")
And the number is unique across all reservations

Scenario: Quick lookup by confirmation number
Given a guest calls with confirmation number "GPH20260415001"
When the agent enters the number in the search field
Then the reservation is immediately displayed with full details
And response time is < 200ms (indexed lookup)

US-003-012: Group Booking CRUD​

As a sales manager, I want to create group bookings for conferences, weddings, and corporate events, So that a block of rooms is reserved under a single group with shared billing and contact details.

Priority: P1 | Points: 8 | Sprint: 4 | Status: Built

Implementation Status​

  • Backend: GroupsController -- full CRUD. POST /groups (SUPER_ADMIN, ADMIN, MANAGER, RESERVATIONS). Group fields: name, code, type (GROUP/ALLOTMENT/BLOCK), arrivalDate, departureDate, roomsBlocked, roomsConfirmed, roomsPickedUp, contactPerson, contactEmail, contactPhone, companyId, totalValue, cutoffDate, notes, status (TENTATIVE/CONFIRMED/CANCELLED). POST /groups/:id/confirm, DELETE /groups/:id (cancel with reason).
  • Frontend: Groups page with list, create/edit forms.

Acceptance Criteria​

Scenario: Create a group booking for a corporate conference
Given TechCorp wants to book 25 rooms for a 3-day conference on May 10-12
When the sales manager creates a group with name "TechCorp Annual Meet",
type GROUP, 25 rooms blocked, cutoff date April 25, contact "Priya Patel",
linked to company "TechCorp Pvt Ltd"
Then the group is created with status TENTATIVE
And 25 rooms are blocked (not yet assigned to individual reservations)

Scenario: Confirm a tentative group
Given the "TechCorp Annual Meet" group is TENTATIVE and the contract is signed
When the sales manager confirms the group via POST /groups/:id/confirm
Then the group status changes to CONFIRMED
And the blocked room count remains at 25

Scenario: Cancel a group past cutoff date
Given the cutoff date has passed and only 10 of 25 rooms have been picked up
When the manager cancels the remaining 15 blocked rooms
Then the group's roomsBlocked is reduced to 10
And the released rooms return to general availability

US-003-013: Group Allotment & Rooming List​

As a sales manager, I want to manage allotment blocks and generate rooming lists for groups, So that individual reservations within the group are tracked and rooms are assigned to named guests.

Priority: P1 | Points: 8 | Sprint: 5 | Status: Partial

Implementation Status​

  • Backend: POST /groups/:id/reservations adds individual reservations to a group. GET /groups/:id/rooming returns the rooming list (list of reservations with guest names and room assignments). Group tracks roomsConfirmed and roomsPickedUp.
  • Gap: No allotment grid UI (date x room-type showing blocked/picked-up/available). No Excel upload for rooming list (bulk guest creation + reservation creation). No cutoff date automated release.

Acceptance Criteria​

Scenario: Add individual reservations to a group
Given the "TechCorp Annual Meet" group has 25 rooms blocked
When the reservations agent adds a reservation for "Ankit Joshi" to the group
with room type Deluxe, May 10-12, rate plan "Corporate Rate"
Then the reservation is created linked to the group (groupId FK)
And the group's roomsPickedUp increments from 0 to 1

Scenario: View rooming list for a group
Given 15 of 25 rooms have been picked up with named guests
When the manager views GET /groups/:id/rooming
Then 15 entries are returned with: guest name, room type, assigned room (if any),
check-in date, check-out date, rate
And 10 remaining rooms show as unassigned/available for pickup

Scenario: Cutoff date enforcement
Given the cutoff date for the group is April 25
When a new room is attempted to be added on April 26
Then a warning is shown: "Cutoff date has passed. Adding rooms requires manager approval."

US-003-014: Waitlist Management​

As a reservations agent, I want to add guests to a waitlist when their desired room type is sold out, So that they are automatically offered a room if availability opens due to cancellations.

Priority: P1 | Points: 5 | Sprint: 5 | Status: Partial

Implementation Status​

  • Backend: WaitlistController -- POST /waitlist (add guest with propertyId, guestId, guestName, guestEmail, guestPhone, roomTypeId, arrivalDate, departureDate, adults, children, notes). GET /waitlist with filters (propertyId, roomTypeId, status). DELETE /waitlist/:id (remove). POST /waitlist/:id/accept (accept offer, creates reservation). Statuses: WAITING, OFFERED, ACCEPTED, EXPIRED, CANCELLED.
  • Gap: No automatic notification when room becomes available (WAITING -> OFFERED transition). No dashboard UI for waitlist management. No prioritization rules (VIP guests first, first-come-first-served, etc.).

Acceptance Criteria​

Scenario: Add guest to waitlist for sold-out dates
Given all Deluxe rooms are sold out for Diwali weekend (Oct 20-22)
When the agent adds guest "Meera Iyer" to the waitlist for Deluxe, Oct 20-22
Then a waitlist entry is created with status WAITING and position in queue
And the guest's contact details are recorded for notification

Scenario: Accept a waitlist offer
Given a cancellation opens up 1 Deluxe room for Oct 20-22
and "Meera Iyer" is first on the waitlist
When the entry status is changed to OFFERED and the agent calls POST /waitlist/:id/accept
Then a reservation is created for "Meera Iyer" for Deluxe, Oct 20-22
And the waitlist entry status changes to ACCEPTED

Scenario: Waitlist offer expires if not accepted
Given an offer was made to a waitlist entry 24 hours ago
When the offer TTL expires without acceptance
Then the waitlist entry status changes to EXPIRED
And the next guest in the queue can be offered the room

US-003-015: Payment Initiation & Verification (Razorpay)​

As a front desk agent or booking engine, I want to collect payment via Razorpay (UPI, credit card, debit card, net banking), So that advance payments or full payments are captured digitally and reconciled automatically.

Priority: P1 | Points: 8 | Sprint: 4 | Status: Built

Implementation Status​

  • Backend: PaymentsController -- POST /payments/initiate (creates Razorpay order), POST /payments/verify (verifies payment with Razorpay), POST /payments/webhook (handles Razorpay webhook with x-razorpay-signature verification), POST /payments/refund, GET /payments/:id/status. Payment model supports modes: CASH, CREDIT_CARD, DEBIT_CARD, UPI, NET_BANKING, WALLET, ROOM_CHARGE, CITY_LEDGER, COMPLIMENTARY, ADJUSTMENT.

Acceptance Criteria​

Scenario: Collect advance payment via UPI
Given a guest confirms a reservation with totalAmount INR 16,500
When the agent initiates a payment of INR 5,000 (advance) via Razorpay
Then a Razorpay order is created with amount 500000 (paise)
And the guest pays via UPI (Google Pay / PhonePe)
And the payment webhook confirms the payment
And the reservation's paidAmount updates to INR 5,000, balanceDue to INR 11,500

Scenario: Process a payment refund
Given a reservation with INR 5,000 paid is cancelled within free cancellation window
When the agent initiates a refund via POST /payments/refund
Then the refund is processed through Razorpay
And the payment status changes to REFUNDED
And the reservation's paidAmount is adjusted

Scenario: Webhook handles delayed payment notification
Given a payment was initiated but the guest's bank took 30 seconds to process
When the Razorpay webhook fires with payment.captured event
Then the signature is verified against the webhook secret
And the payment record is updated to COMPLETED status

As a reservations agent, I want to generate a payment link and send it to the guest via email or WhatsApp, So that the guest can pay in advance without calling the hotel.

Priority: P1 | Points: 5 | Sprint: 6 | Status: Partial

Implementation Status​

  • Backend: Payment link can be generated via Razorpay API. Notification service exists with WhatsApp webhook controller and email templates.
  • Gap: Payment flow is not wired to notification service. No "Send Payment Link" button on reservation detail page. No SMS delivery option.

Acceptance Criteria​

Scenario: Send payment link via WhatsApp
Given a reservation for INR 16,500 has zero advance payment
When the agent clicks "Send Payment Link" and selects WhatsApp
Then a Razorpay payment link is generated for INR 16,500
And a WhatsApp message is sent to the guest's phone with the payment link
And the message includes confirmation number and stay details

Scenario: Payment link expires after 72 hours
Given a payment link was sent 73 hours ago and not used
When the guest clicks the expired link
Then a message shows "This payment link has expired. Please contact the hotel."
And the reservation balance remains unchanged

US-003-017: Reservation Modification History​

As a hotel manager, I want to view a timeline of all changes made to a reservation, So that I can audit who changed what and when, especially for dispute resolution.

Priority: P1 | Points: 3 | Sprint: 5 | Status: Partial

Implementation Status​

  • Backend: ReservationHistory model records every modification: action (CREATED, MODIFIED, CANCELLED, CHECKED_IN, CHECKED_OUT, NO_SHOW), changes (JSON with field-level old/new values), performedBy (userId), performedAt (timestamp). Created via @Audit decorator.
  • Gap: No dedicated API endpoint to retrieve history (it's included in reservation detail). No UI timeline visualization on dashboard.

Acceptance Criteria​

Scenario: View modification history for a reservation
Given reservation GPH20260415001 was created, then dates were extended, then room was upgraded
When the manager views the reservation history
Then 3 history entries are shown in chronological order:
1. CREATED by "reservations@hotel.com" at 2026-03-01 14:30
2. MODIFIED by "frontdesk@hotel.com" at 2026-03-15 10:00 - departureDate changed from Apr 18 to Apr 20
3. MODIFIED by "frontdesk@hotel.com" at 2026-04-01 09:15 - roomTypeId changed from Standard to Deluxe
And each entry shows the user who made the change and the timestamp

US-003-018: Overbooking Control Configuration​

As a revenue manager, I want to configure overbooking limits per room type, So that the hotel can maximize occupancy while controlling the risk of walking guests.

Priority: P2 | Points: 5 | Sprint: 8 | Status: Partial

Implementation Status​

  • Backend: OverbookingController exists in analytics-service. Basic overbooking percentage tracking.
  • Gap: Not configurable per room type from dashboard. No walk-list management when overbooking triggers. No historical overbooking analysis (how often did overbooking result in walks).

Acceptance Criteria​

Scenario: Set overbooking percentage per room type
Given the property has 20 Deluxe rooms
When the revenue manager sets overbooking to 10% for Deluxe
Then the availability engine allows up to 22 bookings (20 + 10%) for Deluxe
And a warning is shown on the dashboard when occupancy exceeds 100%

Scenario: Zero overbooking for premium rooms
Given the property has 5 Presidential Suites
When the revenue manager sets overbooking to 0% for Presidential Suite
Then the system strictly prevents more than 5 bookings for that room type

US-003-019: Cancellation Charge Auto-Calculation​

As a reservations agent, I want the system to automatically calculate cancellation charges based on the linked policy, So that charges are consistent and I don't need to manually look up policy rules.

Priority: P2 | Points: 5 | Sprint: 8 | Status: Not Started

Implementation Status​

  • Backend: Not implemented. Cancellation policies exist with rule definitions, but the cancellation endpoint does not auto-calculate charges based on days-before-arrival logic.
  • Frontend: No auto-calculation display during cancellation flow.

Acceptance Criteria​

Scenario: Auto-calculate cancellation charge based on policy
Given a reservation with "Flexible" policy (100% first night within 3 days of arrival)
and arrival is 2 days away, first night rate INR 5,500
When the agent initiates cancellation
Then the system auto-calculates: "Cancellation charge: INR 5,500 (100% of first night)"
And the agent can confirm or override the charge with manager approval

Scenario: Free cancellation within policy window
Given a reservation with "Flexible" policy and arrival is 10 days away
When the agent initiates cancellation
Then the system shows: "No cancellation charge applies (cancelled 10 days before arrival)"

US-003-020: Charge Routing Rules​

As a hotel accountant, I want to configure routing rules that split charges across multiple folios, So that room charges go to the corporate folio and personal expenses go to the guest folio.

Priority: P2 | Points: 5 | Sprint: 6 | Status: Built

Implementation Status​

  • Backend: RoutingRulesController exists with configuration for charge routing (room charges, F&B, tax to separate folios).

Acceptance Criteria​

Scenario: Route room charges to corporate folio and F&B to guest folio
Given a corporate reservation has two folios: "TechCorp" (company) and "Personal" (guest)
When a routing rule is set: Room + Tax -> TechCorp folio, F&B + Minibar -> Personal folio
Then room charges are auto-posted to the TechCorp folio
And F&B room service charges are auto-posted to the Personal folio

US-003-021: Bulk Group Check-In & Room Assignment​

As a front desk supervisor, I want to check in an entire group at once with pre-assigned rooms, So that a 25-person conference group doesn't create a 30-minute check-in queue.

Priority: P2 | Points: 8 | Sprint: 10 | Status: Not Started

Implementation Status​

  • Backend: Not implemented. Individual check-in exists but no bulk operation.
  • Frontend: Not implemented.

Acceptance Criteria​

Scenario: Bulk check-in for a group with pre-assigned rooms
Given the "TechCorp Annual Meet" group has 25 reservations, all with rooms assigned
When the front desk supervisor clicks "Group Check-In"
Then all 25 reservations are checked in simultaneously
And key cards for all rooms are queued for encoding
And a summary is shown: 25 checked in, 0 failed

Scenario: Partial bulk check-in with errors
Given 25 reservations in the group, but 2 rooms are out of order
When bulk check-in is attempted
Then 23 are checked in successfully
And 2 are flagged: "Room 305 is OUT_OF_ORDER - reassign room before check-in"

US-003-022: Availability Calendar Visualization​

As a revenue manager, I want to view a visual calendar showing daily availability per room type, So that I can quickly spot sold-out dates, low-demand periods, and plan pricing strategies.

Priority: P2 | Points: 5 | Sprint: 8 | Status: Partial

Implementation Status​

  • Backend: GET /inventory/:propertyId/:roomTypeId?startDate=&endDate= returns daily inventory breakdown (total, sold, blocked, available).
  • Gap: No dashboard calendar UI. No color-coded heat map for occupancy levels. No multi-room-type comparison view.

Acceptance Criteria​

Scenario: View 30-day availability calendar
Given the property has 3 room types with varying availability
When the revenue manager opens the availability calendar for April 2026
Then a grid shows: rows = room types, columns = dates (1-30)
And each cell displays available room count, color-coded by occupancy:
Green (< 70%), Yellow (70-90%), Red (> 90%), Black (sold out)

Scenario: Identify demand gap for pricing action
Given mid-week dates (Mon-Wed) consistently show green (low occupancy)
When the revenue manager reviews the calendar
Then they can identify the pattern and navigate to rate management
to create a mid-week promotional rate