Skip to main content

EP-002: Property & Room Management

Business Objective​

A hotel's inventory is its core revenue asset. Accurate property configuration, room type definitions, and real-time room status tracking are foundational to every downstream operation -- reservations, housekeeping, front desk, and channel management. This epic ensures that hotel operators can onboard a property rapidly (target: under 2 hours for a 100-room hotel), manage room inventory with precision, and keep room status synchronized across all departments. Incorrect room status or stale inventory data directly translates to lost revenue (overbooking penalties, OTA parity violations, or unsold rooms).

Audience​

  • Primary: Admin, Manager (property setup and ongoing configuration)
  • Secondary: Front Desk (room status view), Housekeeping (status updates), Reservations (availability dependency)
  • Tertiary: Super Admin (multi-property chain oversight)

Scope​

In Scope​

  • Property CRUD (create, read, update, soft-delete) with address, GSTIN, PAN, star rating, timezone, currency
  • Room type CRUD with amenities list, photo gallery (JSON images array), bed type, max occupancy, base rate, extra adult/child rates, size (sqm)
  • Room inventory CRUD -- create rooms with room number, floor, building, features (smoking, accessible, connecting), sort order
  • Room status lifecycle with 5 housekeeping statuses (CLEAN, DIRTY, INSPECTED, PICKUP, OUT_OF_ORDER/OUT_OF_SERVICE)
  • Room status summary endpoint (counts per status per property)
  • Room notes management (front desk and housekeeping can annotate rooms)
  • Room status change logging (audit trail with previous/new status, changed-by, reason)
  • Rate plan CRUD with meal plan (EP/CP/MAP/AP), payment policy, min/max stay, date-level rates, restrictions (CTA, CTD, min LOS)
  • Cancellation policy CRUD with rule-based charge calculation (days before arrival, charge percentage)
  • Property onboarding wizard (guided setup with progress tracking)
  • Data migration from legacy PMS (CSV/XML imports for eZee, Hotelogix, Opera; OTA channel re-mapping; reconciliation reports)
  • Multi-building support (building field on rooms)
  • Redis caching for property and room data

Out of Scope​

  • Floor plan visual editor (drag-and-drop room placement) -- deferred to Phase 3
  • 3D room virtual tours -- future mobile app feature
  • Dynamic pricing / revenue management engine -- covered in EP-012 (Revenue Management)
  • Channel manager rate push -- covered in EP-009 (Channel Manager)

Current Implementation Status​

  • Backend (property-service): Fully built. PropertiesController with POST/GET/PUT/DELETE. @Roles('SUPER_ADMIN', 'ADMIN') for create/delete, 'MANAGER' added for update. @CurrentTenant() decorator for tenant scoping. PaginationDto support. Redis cache layer in service.
  • Backend (room-types): Fully built. RoomTypesController with CRUD. Scoped to property via GET /properties/:propertyId/room-types. Amenities stored as String[], images as Json.
  • Backend (rooms): Fully built. RoomsController with CRUD, status update (PATCH /rooms/:id/status with @CurrentUser() for audit), notes update (PATCH /rooms/:id/notes), room summary (GET /properties/:propertyId/rooms/summary). Filters by roomTypeId, status, floor, isOccupied.
  • Backend (rate-plans): Fully built. RatePlansController with CRUD, date-level rates (GET/PUT /rate-plans/:id/rates), bulk rate update, restrictions (POST/GET /rate-plans/:id/restrictions).
  • Backend (cancellation-policies): Fully built. CRUD with default policy endpoint (GET /properties/:propertyId/cancellation-policies/default).
  • Backend (onboarding): OnboardingController with POST /onboarding (start) and GET /onboarding/:propertyId/status. Roles restricted to SUPER_ADMIN, ADMIN.
  • Backend (migration): Extensive. MigrationController with CSV imports (rooms, guests, folios, POS menu), plus dedicated importers for eZee (CSV), Hotelogix (XML), Opera (CSV). Each supports guests, room-types, rooms, reservations, rate-plans. OTA re-mapping (detect, apply, getExistingChannels). Reconciliation report generation.
  • Dashboard: Settings pages for property, room types, rooms. Setup wizard with step tracker.
  • Tests: Unit tests for property service, room status transitions.

Gap Analysis​

  • Floor Plan Visual Editor: No implementation. The Room model has floor and building fields but no spatial coordinates (x/y) for visual placement. Hotels with 200+ rooms need a visual map to quickly identify room locations. Impact: Medium -- workaround is list/grid view.
  • Room Photos Gallery UI: Backend stores images as JSON array on RoomType, and UploadsController generates pre-signed S3 URLs. Dashboard UI for uploading/reordering photos is incomplete (upload button exists but drag-to-reorder and bulk upload missing). Impact: Low -- photos can be managed via API.
  • Multi-Building Support: The building field exists on Room model and can be filtered, but there is no dedicated Building entity (no building-level settings, no building summary dashboard). Impact: Medium for resort properties with multiple wings.
  • Legacy PMS Migration -- Additional Formats: eZee, Hotelogix, and Opera importers are built. Missing: IDS Next (FX/Fortune), Protel, Clock PMS, and Guestline exports. Also missing: automated field mapping detection (currently requires manual column mapping). Impact: High for onboarding hotels migrating from less common PMS systems.
  • Rate Calendar UI: Backend supports date-level rates and restrictions, but dashboard lacks a visual rate calendar grid (date x room-type matrix). Revenue managers need this for at-a-glance rate management. Impact: Medium.

User Stories​

Story IDTitlePriorityPointsStatusSprint
US-002-001Property CRUDP05Built1
US-002-002Room Type CRUD with Amenities & PhotosP05Built1
US-002-003Room Inventory CRUDP05Built1
US-002-004Room Status Lifecycle ManagementP05Built2
US-002-005Room Status Summary DashboardP03Built2
US-002-006Property Onboarding WizardP08Partial2
US-002-007Rate Plan ConfigurationP18Built3
US-002-008Cancellation Policy ManagementP15Built3
US-002-009Date-Level Rate & Restriction ManagementP15Built3
US-002-010Room Features & Special AttributesP13Built2
US-002-011Legacy PMS Data Migration (eZee, Hotelogix, Opera)P213Partial6
US-002-012Multi-Building / Wing SupportP25Partial8
US-002-013Floor Plan Visual EditorP213Not Started12
US-002-014Room Photo Gallery ManagementP25Partial8

Dependencies​

  • EP-001 (Auth & Access Control): Property CRUD requires authenticated users with ADMIN/SUPER_ADMIN roles.
  • EP-003 (Reservation Management): Reservations depend on room types, rooms, and rate plans existing.
  • EP-009 (Channel Manager): Channel room/rate mappings reference room types and rate plans.

Acceptance Criteria (Epic Level)​

  • Admin can create a property with all India-specific fields (GSTIN, PAN, timezone Asia/Kolkata, currency INR)
  • Room types can be configured with amenities, photos, occupancy limits, and base rates
  • Rooms can be created, assigned to room types, and filtered by floor/status/type
  • Room status can be updated by Front Desk and Housekeeping staff with full audit trail
  • Rate plans with date-level pricing and restrictions can be created and bulk-updated
  • Cancellation policies with rule-based charges are linked to rate plans
  • Onboarding wizard guides a new property from zero to operational in under 2 hours
  • Data can be imported from at least 3 legacy PMS systems (eZee, Hotelogix, Opera)
  • Property and room data is cached in Redis with appropriate TTL and invalidation

KPIs / Success Metrics​

  • Property onboarding time: < 2 hours for 100-room hotel
  • Room status update latency: < 200ms (cached reads)
  • Room status accuracy: 99.5% (status matches physical room state within 15 minutes)
  • Data migration success rate: > 95% record import without manual intervention
  • Room type configuration completeness: > 90% of room types have photos, amenities, and rates configured

Detailed User Stories​

US-002-001: Property CRUD​

As a hotel admin, I want to create and manage property records with all operational details, So that the property is correctly configured in the system with accurate GSTIN, address, and settings for downstream operations.

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

Implementation Status​

  • Backend: PropertiesController -- POST /properties (SUPER_ADMIN, ADMIN), GET /properties (tenant-scoped via @CurrentTenant()), GET /properties/:id, PUT /properties/:id (SUPER_ADMIN, ADMIN, MANAGER), DELETE /properties/:id (SUPER_ADMIN, ADMIN, returns 204). CreatePropertyDto and UpdatePropertyDto with validation. Redis cache on reads.
  • Frontend: Settings > Property page with form fields for name, code, type (HOTEL/RESORT/SERVICE_APARTMENT/BOUTIQUE/MOTEL/HOSTEL), star rating, address, contact, GSTIN, PAN, timezone, currency.

Acceptance Criteria​

Scenario: Create a new property with Indian tax details
Given the admin is logged in with ADMIN role
When they submit a new property with name "Grand Palace Hotel", code "GPH",
type "HOTEL", star rating 4, city "Mumbai", state "Maharashtra",
GSTIN "27AABCU9603R1ZX", PAN "AABCU9603R", timezone "Asia/Kolkata", currency "INR"
Then the property is created with a UUID and returned with status 201
And the property appears in GET /properties scoped to the admin's tenant

Scenario: Prevent duplicate property code within tenant
Given a property with code "GPH" already exists for the current tenant
When the admin attempts to create another property with code "GPH"
Then the system returns 400 with message "Property code already exists for this tenant"

Scenario: Update property GSTIN for GST compliance
Given the property "Grand Palace Hotel" exists with no GSTIN
When the manager updates the property with GSTIN "27AABCU9603R1ZX"
Then the property record is updated and the Redis cache is invalidated
And subsequent GET requests return the updated GSTIN

US-002-002: Room Type CRUD with Amenities & Photos​

As a hotel manager, I want to define room types with detailed attributes including amenities, photos, and pricing, So that guests and booking channels have accurate room information for booking decisions.

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

Implementation Status​

  • Backend: RoomTypesController -- full CRUD. POST /room-types, GET /properties/:propertyId/room-types, GET /room-types/:id, PUT /room-types/:id, DELETE /room-types/:id. Amenities as String[], images as Json (array of objects with url, caption, sortOrder). Bed type, max occupancy/adults/children, base rate, extra adult/child rate, size in sqm.
  • Frontend: Settings > Room Types page with form, amenity multi-select, image upload section.

Acceptance Criteria​

Scenario: Create a Deluxe Room type with amenities and pricing
Given the manager navigates to Settings > Room Types
When they create a room type with code "DLX", name "Deluxe Room",
max occupancy 3, max adults 2, max children 1, bed type "King",
base rate INR 5500, extra adult rate INR 1200, extra child rate INR 600,
amenities ["WiFi", "AC", "Mini Bar", "Tea/Coffee Maker", "Safe Locker"],
size 32 sqm
Then the room type is created with status 201
And the room type appears in the property's room type list

Scenario: Upload room type photos
Given the Deluxe Room type exists
When the manager uploads 5 photos with captions
Then the images JSON array stores each photo with url, caption, and sortOrder
And the photos are retrievable via the room type GET endpoint

Scenario: Prevent deletion of room type with active rooms
Given the Deluxe Room type has 20 rooms assigned to it
When the manager attempts to delete the room type
Then the system returns 409 with message "Cannot delete room type with active rooms"

US-002-003: Room Inventory CRUD​

As a hotel admin, I want to create and manage individual rooms assigned to room types, So that the property has an accurate room inventory for reservations and front desk operations.

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

Implementation Status​

  • Backend: RoomsController -- POST /rooms (SUPER_ADMIN, ADMIN, MANAGER), GET /properties/:propertyId/rooms with filters (roomTypeId, status, floor, isOccupied), GET /rooms/:id, PUT /rooms/:id, DELETE /rooms/:id (204). Room fields: roomNumber, floor, building, features (String[]), isSmoking, notes, sortOrder, isActive.
  • Frontend: Settings > Rooms page with data table, filters, create/edit dialogs.

Acceptance Criteria​

Scenario: Create rooms on the third floor
Given the Deluxe Room type "DLX" exists for the property
When the admin creates rooms 301, 302, 303, 304, 305 on floor "3",
assigned to room type "DLX", with features ["balcony", "city-view"]
Then 5 rooms are created, each with default status VACANT_CLEAN and isOccupied false
And the room numbers are unique within the property

Scenario: Filter rooms by status and floor
Given the property has 100 rooms across 5 floors with various statuses
When the front desk agent filters rooms by status "VACANT_CLEAN" and floor "3"
Then only rooms on floor 3 with VACANT_CLEAN status are returned
And the response includes pagination metadata

Scenario: Mark a room as smoking-accessible
Given room 305 exists in the inventory
When the manager updates room 305 with isSmoking true and features ["accessible", "connecting-to-306"]
Then the room record reflects isSmoking true and updated features array
And the room appears in accessibility-filtered searches

US-002-004: Room Status Lifecycle Management​

As a front desk agent or housekeeping supervisor, I want to update room status through its lifecycle (CLEAN, DIRTY, INSPECTED, OUT_OF_ORDER, OUT_OF_SERVICE), So that all departments have real-time visibility into which rooms are available, need cleaning, or are under maintenance.

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

Implementation Status​

  • Backend: PATCH /rooms/:id/status with UpdateRoomStatusDto. Roles: SUPER_ADMIN, ADMIN, MANAGER, FRONT_DESK, HOUSEKEEPING. @CurrentUser() captures who changed the status. RoomStatusLog model stores previous/new status, changedBy, reason, timestamp. Separate FrontOfficeStatus (VACANT, OCCUPIED, DUE_OUT, CHECKED_OUT) and HousekeepingStatus (CLEAN, DIRTY, INSPECTED, PICKUP) enums.
  • Frontend: Room grid in Front Desk module with color-coded status badges. Housekeeping floor view with status toggle.
  • Events: room.status.changed Kafka event published on every status change.

Acceptance Criteria​

Scenario: Housekeeping marks a room as clean after checkout cleaning
Given room 301 has housekeeping status DIRTY after guest checkout
When the housekeeping attendant updates room 301 status to CLEAN
Then the room status log records: previousStatus DIRTY, newStatus CLEAN,
changedBy "<attendant-user-id>", timestamp now
And a Kafka event "room.status.changed" is published with roomId and new status
And the room summary endpoint reflects one fewer DIRTY room and one more CLEAN room

Scenario: Supervisor inspects a cleaned room
Given room 301 has housekeeping status CLEAN
When the supervisor updates status to INSPECTED with reason "Passed inspection"
Then the status changes to INSPECTED and the front office status becomes VACANT
And the room becomes available for check-in assignment

Scenario: Place a room out of order for maintenance
Given room 412 has a plumbing issue
When the manager sets room 412 to OUT_OF_ORDER with reason "Bathroom leak - plumber scheduled"
Then room 412 is excluded from availability calculations
And the room appears with OUT_OF_ORDER status on the front desk room grid
And the status change is logged with the maintenance reason

US-002-005: Room Status Summary Dashboard​

As a front desk manager, I want to see a summary of room statuses at a glance, So that I can quickly assess how many rooms are available, occupied, dirty, or out of order during shift handover.

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

Implementation Status​

  • Backend: GET /properties/:propertyId/rooms/summary returns counts grouped by status (VACANT_CLEAN, VACANT_DIRTY, OCCUPIED, OUT_OF_ORDER, OUT_OF_SERVICE). Cached in Redis with short TTL, invalidated on status change.
  • Frontend: Dashboard widget showing room status pie chart and counts.

Acceptance Criteria​

Scenario: View room status summary during morning shift
Given the property has 100 rooms: 40 OCCUPIED, 25 VACANT_CLEAN, 20 VACANT_DIRTY,
12 INSPECTED, 2 OUT_OF_ORDER, 1 OUT_OF_SERVICE
When the front desk manager calls GET /properties/:propertyId/rooms/summary
Then the response returns counts for each status category
And the total equals the property's active room count (100)

Scenario: Summary updates in real-time after status change
Given the room summary shows 25 VACANT_CLEAN rooms
When housekeeping marks one more room as CLEAN (was DIRTY)
Then the Redis cache is invalidated
And the next summary request shows 26 VACANT_CLEAN and 19 VACANT_DIRTY

US-002-006: Property Onboarding Wizard​

As a hotel admin setting up a new property, I want a guided onboarding wizard that walks me through property details, room types, rooms, rate plans, and policies, So that I can get the property operational quickly without missing critical configuration steps.

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

Implementation Status​

  • Backend: OnboardingController -- POST /onboarding starts the workflow (SUPER_ADMIN, ADMIN), GET /onboarding/:propertyId/status returns progress with step completion flags.
  • Frontend: Setup wizard with step tracker in dashboard. Steps: Property Details, Room Types, Rooms, Rate Plans, Policies, Staff Users.
  • Gap: Wizard does not auto-populate room numbers from a template (e.g., "Floor 1: 101-110, Floor 2: 201-210"). Tax configuration step (GST slab setup for different room tariffs) is missing. No "import from spreadsheet" option within the wizard flow.

Acceptance Criteria​

Scenario: Complete onboarding wizard for a 50-room hotel
Given the admin starts the onboarding wizard for a new property
When they complete all steps: Property Details (name, address, GSTIN),
Room Types (3 types: Standard, Deluxe, Suite),
Rooms (50 rooms across 5 floors), Rate Plans (Rack Rate, Corporate Rate),
Cancellation Policies (Flexible, Non-Refundable), Staff Users (5 users)
Then the onboarding status shows 100% completion
And the property is marked as operational
And all configured entities are accessible via their respective APIs

Scenario: Resume partially completed onboarding
Given the admin completed Property Details and Room Types but stopped
When they return to the onboarding wizard
Then steps 1 and 2 show as completed with green checkmarks
And step 3 (Rooms) is highlighted as the current step
And previously entered data is preserved

Scenario: Validate mandatory fields before advancing steps
Given the admin is on the Property Details step
When they attempt to proceed without entering GSTIN
Then a validation warning shows "GSTIN is recommended for GST invoice generation"
And the admin can choose to skip (soft warning) or fill it in

US-002-007: Rate Plan Configuration​

As a revenue manager, I want to create rate plans with meal plan inclusions, payment policies, and validity periods, So that the hotel can offer differentiated pricing for different market segments (corporate, OTA, direct).

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

Implementation Status​

  • Backend: RatePlansController -- full CRUD. Fields: code, name, description, mealPlan (EP/CP/MAP/AP), inclusions (String[]), cancellationPolicyId (FK), paymentPolicy (PAY_NOW/PAY_AT_HOTEL/PARTIAL_PREPAYMENT), minStay, maxStay, minAdvanceBooking, maxAdvanceBooking, validFrom, validTo, isRefundable. Linked to cancellation policies. Date-level rates via RatePlanRate model. Restrictions endpoint for CTA/CTD/min-LOS.
  • Frontend: Settings > Rate Plans page with form including meal plan dropdown, policy association, validity date picker.

Acceptance Criteria​

Scenario: Create a corporate rate plan with CP meal plan
Given the property has room types and a flexible cancellation policy configured
When the revenue manager creates a rate plan with code "CORP", name "Corporate Rate",
meal plan CP (Continental Plan - Breakfast included), payment policy PAY_AT_HOTEL,
minimum stay 1 night, cancellation policy "Flexible", valid from 2026-04-01 to 2027-03-31
Then the rate plan is created and linked to the cancellation policy
And the rate plan is available for reservation creation

Scenario: Bulk update date-level rates for peak season
Given the "Rack Rate" rate plan exists for Deluxe Room type
When the revenue manager bulk updates rates via PUT /rate-plans/:id/rates
with dates 2026-12-20 to 2026-01-05, rate INR 8500 (up from INR 5500),
extra adult INR 1500
Then 17 date-level rate records are created/updated
And availability checks for those dates reflect the peak season pricing

Scenario: Set minimum length of stay restriction for Diwali week
Given the Rack Rate plan exists
When the revenue manager sets restrictions for 2026-10-20 to 2026-10-26
with minLOS 3, closedToArrival false, closedToDeparture false
Then bookings for those dates require a minimum 3-night stay
And availability responses include the restriction metadata

US-002-008: Cancellation Policy Management​

As a hotel admin, I want to define cancellation policies with tiered charge rules, So that the hotel is protected against last-minute cancellations while offering flexibility for advance bookings.

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

Implementation Status​

  • Backend: CancellationPoliciesController -- full CRUD. rules stored as JSON array: [{ daysBeforeArrival, chargeType, chargeValue }]. Default policy per property via GET /properties/:propertyId/cancellation-policies/default. isDefault flag.
  • Frontend: Settings > Cancellation Policies page with rule builder.

Acceptance Criteria​

Scenario: Create a flexible cancellation policy
Given the admin navigates to Settings > Cancellation Policies
When they create a policy named "Flexible" with rules:
- Cancel 7+ days before arrival: 0% charge
- Cancel 3-7 days before arrival: 50% of first night
- Cancel 0-3 days before arrival: 100% of first night
Then the policy is created and available for linking to rate plans

Scenario: Set default cancellation policy for a property
Given "Flexible" and "Non-Refundable" policies exist
When the admin marks "Flexible" as the default policy
Then new rate plans without an explicit cancellation policy inherit "Flexible"
And GET /properties/:propertyId/cancellation-policies/default returns "Flexible"

Scenario: Prevent deletion of policy linked to active rate plans
Given the "Flexible" policy is linked to 3 active rate plans
When the admin attempts to delete the "Flexible" policy
Then the system returns 409 with message "Cannot delete policy linked to active rate plans"

US-002-009: Date-Level Rate & Restriction Management​

As a revenue manager, I want to set day-by-day rates and booking restrictions per rate plan and room type, So that pricing responds to demand patterns (weekends, festivals, events) and inventory is controlled.

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

Implementation Status​

  • Backend: RatePlanRate model with composite unique key (ratePlanId + roomTypeId + date). GET /rate-plans/:id/rates?startDate=&endDate=&roomTypeId=, PUT /rate-plans/:id/rates (bulk upsert). POST /rate-plans/:id/restrictions and GET /rate-plans/:id/restrictions?startDate=&endDate= for CTA/CTD/minLOS.
  • Frontend: Rate grid UI exists but lacks visual calendar-style interface.

Acceptance Criteria​

Scenario: View rate calendar for a 30-day period
Given the Rack Rate plan has date-level rates configured for April 2026
When the revenue manager calls GET /rate-plans/:id/rates
with startDate "2026-04-01" and endDate "2026-04-30" and roomTypeId for Deluxe
Then the response returns 30 daily rate entries with rate, extraAdult, extraChild amounts

Scenario: Apply weekend surcharge
Given the Rack Rate has a base rate of INR 5500 for Standard rooms
When the revenue manager bulk updates Saturday and Sunday rates to INR 6500
for the next 3 months (approximately 26 weekend dates)
Then all specified dates are updated via a single PUT request
And the availability engine returns the correct rate based on check-in date

Scenario: Close arrivals during a sold-out conference period
Given a major conference is expected from March 10-12, 2026
When the revenue manager sets closedToArrival true for March 10-12 on the OTA rate plan
Then no new bookings can arrive on those dates via OTA channels
And existing confirmed reservations for those dates are unaffected

US-002-010: Room Features & Special Attributes​

As a reservations agent, I want to tag rooms with special features (smoking, accessible, connecting, sea-view, pool-facing), So that I can match guest preferences to the right room during assignment.

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

Implementation Status​

  • Backend: Room model has isSmoking (Boolean), features (String[] -- flexible tags), building (String), floor (String). Filter support on GET /properties/:propertyId/rooms.
  • Frontend: Room edit form has smoking toggle and feature tag input.

Acceptance Criteria​

Scenario: Tag rooms with accessibility features
Given rooms 101 and 102 are wheelchair-accessible with grab bars
When the admin updates these rooms with features ["accessible", "grab-bars", "roll-in-shower"]
Then the features are stored as a String array on each room record
And front desk can filter rooms by feature to assign accessible rooms to guests who need them

Scenario: Identify connecting rooms for family bookings
Given rooms 305 and 306 have a connecting door
When the admin tags room 305 with features ["connecting-to-306"]
and room 306 with features ["connecting-to-305"]
Then the reservations agent can identify connecting rooms for families booking multiple rooms

US-002-011: Legacy PMS Data Migration (eZee, Hotelogix, Opera)​

As a hotel admin migrating from an existing PMS, I want to import historical data (guests, rooms, room types, reservations, rate plans) from my legacy system, So that we retain guest history and operational continuity when switching to HMP.

Priority: P2 | Points: 13 | Sprint: 6 | Status: Partial

Implementation Status​

  • Backend: MigrationController with comprehensive import endpoints:
    • Legacy CSV: POST /migration/:propertyId/rooms, /guests, /folios, /pos-menu
    • eZee CSV: POST /migration/import/ezee/{guests,room-types,rooms,reservations,rate-plans}
    • Hotelogix XML: POST /migration/import/hotelogix/{guests,room-types,rooms,reservations,rate-plans}
    • Opera CSV: POST /migration/import/opera/{guests,room-types,rooms,reservations,rate-plans}
    • OTA Re-mapping: POST /migration/ota-remap/detect, /ota-remap/:propertyId/apply
    • Reconciliation: POST /migration/reconciliation/:propertyId
    • Services: EzeeImporterService, HotelogixImporterService, OperaImporterService, OtaRemapperService, ReconciliationService
  • Gap: No dashboard UI for migration (API-only). No support for IDS Next (FX/Fortune), Protel, Clock PMS formats. No automated field mapping -- admin must prepare CSV in expected format. No progress tracking for large imports.

Acceptance Criteria​

Scenario: Import guest data from eZee CSV export
Given the admin has exported 2,500 guest records from eZee as CSV
When they POST the CSV data to /migration/import/ezee/guests with the propertyId
Then guests are imported with name, email, phone, nationality mapped correctly
And the response includes counts: imported 2,450, skipped 30 (duplicates), errors 20 (malformed)
And a reconciliation report confirms source count matches within 2% tolerance

Scenario: Migrate rooms and room types from Opera
Given the admin has Opera CSV exports for room types and rooms
When they import room types first, then rooms (linking to imported room types)
Then room types are created with amenities and rates mapped from Opera fields
And rooms are created with correct room numbers, floors, and room type associations

Scenario: Auto-detect and remap OTA channel sources
Given the imported reservations have source values like "BDC", "EXP", "AGD"
When the admin calls POST /migration/ota-remap/detect with those source values
Then the system returns detected mappings: "BDC" -> BOOKING_COM, "EXP" -> EXPEDIA, "AGD" -> AGODA
And the admin can apply these mappings to normalize booking sources

US-002-012: Multi-Building / Wing Support​

As a resort manager with multiple buildings/wings, I want to organize rooms by building, So that guests can be assigned to their preferred building and housekeeping routes are optimized per wing.

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

Implementation Status​

  • Backend: Room model has building field (String, nullable). Rooms can be filtered by building in the list endpoint. No dedicated Building entity.
  • Gap: No building-level configuration (name, description, floor count, amenities per building). No building summary counts. No building-based housekeeping assignment optimization.

Acceptance Criteria​

Scenario: Organize rooms by building for a resort
Given "Lakeside Resort" has three buildings: Main Block, Pool Wing, Garden Cottage
When the admin creates rooms with building field set to "Main Block", "Pool Wing", or "Garden Cottage"
Then rooms can be filtered by building via GET /properties/:propertyId/rooms?building=Pool Wing
And the front desk can assign rooms in the guest's preferred building

Scenario: View room summary by building
Given rooms are tagged with building names
When the manager requests room summary filtered by building "Main Block"
Then the summary shows status counts only for rooms in Main Block
And the total matches the number of rooms in that building

US-002-013: Floor Plan Visual Editor​

As a hotel manager, I want to create a visual floor plan showing room positions, corridors, and facilities, So that the front desk can visually select rooms during check-in and housekeeping can plan efficient cleaning routes.

Priority: P2 | Points: 13 | Sprint: 12 | Status: Not Started

Implementation Status​

  • Backend: Not implemented. No spatial coordinates on Room model. No floor plan storage.
  • Frontend: Not implemented.
  • Note: The POS module's POSTable model has posX/posY fields for table placement, providing a pattern for room positioning.

Acceptance Criteria​

Scenario: Design a floor plan for the third floor
Given the admin opens the Floor Plan Editor for Floor 3
When they drag room boxes onto a canvas, position them along corridors,
and add landmarks (elevator, staircase, housekeeping closet)
Then the floor plan is saved with room positions (x, y coordinates)
And the front desk can click on a room in the visual plan to view its status and guest info

Scenario: Live room status on floor plan
Given the floor plan for Floor 3 is configured with 20 rooms
When the front desk views the floor plan
Then each room is color-coded: green (VACANT_CLEAN), red (OCCUPIED),
yellow (DIRTY), blue (INSPECTED), grey (OUT_OF_ORDER)
And clicking a room shows a popup with guest name (if occupied), status, and quick actions

As a hotel marketing manager, I want to upload, reorder, and manage photos for each room type, So that the booking engine and OTA channels display attractive, accurate room images.

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

Implementation Status​

  • Backend: RoomType.images stored as JSON array. UploadsController generates pre-signed S3 URLs via POST /uploads/presigned-url (accepts fileName, contentType, folder).
  • Frontend: Photo upload button exists on room type edit page. Missing: drag-to-reorder, bulk upload, image cropping, caption editing inline.
  • Gap: No image compression/resizing on upload. No automatic thumbnail generation for different display contexts (listing vs. detail vs. OTA feed).

Acceptance Criteria​

Scenario: Upload multiple photos for a room type
Given the manager is editing the "Deluxe Room" room type
When they upload 8 photos (bedroom, bathroom, view, amenities, etc.)
Then each photo is uploaded to S3 via pre-signed URL
And the images JSON array on the room type contains 8 entries with url, caption, and sortOrder

Scenario: Reorder photos to set hero image
Given the Deluxe Room type has 8 photos uploaded
When the manager drags the "City View" photo to position 1
Then the sortOrder values are updated so "City View" has sortOrder 0
And the booking engine displays "City View" as the primary image

Scenario: Delete an outdated photo
Given the Deluxe Room was recently renovated
When the manager deletes the old bathroom photo from the gallery
Then the image entry is removed from the JSON array
And the S3 object is marked for cleanup