From 7115d34921951503674a378e6788a5673bf6ad52 Mon Sep 17 00:00:00 2001 From: telivity-otaip Date: Wed, 5 Aug 2026 01:58:17 -0500 Subject: [PATCH] =?UTF-8?q?fix(api):=20effective-rate=20query=20whitelist?= =?UTF-8?q?=20+=20vacant=5Fclean=E2=86=92vacant=5Fdirty=20transition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Day Zero P0s from haip-qa staging run: 1. getEffectiveRate binds @Query() to EffectiveRateQueryDto while the global ValidationPipe runs forbidNonWhitelisted over the whole query object — propertyId was rejected ("property propertyId should not exist"), so every effective-rate call 400'd. Added optional propertyId to the DTO. 2. VALID_TRANSITIONS blocked vacant_clean → vacant_dirty, the KB 5.2-documented "clean room found dirty" path the desk needs. Added it. --- .../modules/rate-plan/dto/effective-rate-query.dto.ts | 11 ++++++++++- apps/api/src/modules/room/room-status.service.ts | 4 +++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/api/src/modules/rate-plan/dto/effective-rate-query.dto.ts b/apps/api/src/modules/rate-plan/dto/effective-rate-query.dto.ts index 6d5aea5c..ec223137 100644 --- a/apps/api/src/modules/rate-plan/dto/effective-rate-query.dto.ts +++ b/apps/api/src/modules/rate-plan/dto/effective-rate-query.dto.ts @@ -1,7 +1,16 @@ import { ApiPropertyOptional } from '@nestjs/swagger'; -import { IsDateString, IsInt, IsOptional, Min } from 'class-validator'; +import { IsDateString, IsInt, IsOptional, IsUUID, Min } from 'class-validator'; export class EffectiveRateQueryDto { + // The controller also binds propertyId via @Query('propertyId'), but the + // global ValidationPipe validates the WHOLE query object against this DTO — + // without this field every call 400s with "property propertyId should not + // exist" (forbidNonWhitelisted). Day Zero P0. + @ApiPropertyOptional({ description: 'Property ID (also bound by the controller)' }) + @IsOptional() + @IsUUID() + propertyId?: string; + @ApiPropertyOptional({ description: 'Length of stay in nights (alternative to checkIn/checkOut)' }) @IsOptional() @IsInt() diff --git a/apps/api/src/modules/room/room-status.service.ts b/apps/api/src/modules/room/room-status.service.ts index 3f417694..0f12571c 100644 --- a/apps/api/src/modules/room/room-status.service.ts +++ b/apps/api/src/modules/room/room-status.service.ts @@ -20,7 +20,9 @@ type RoomStatus = | 'out_of_service'; const VALID_TRANSITIONS: Record = { - vacant_clean: ['occupied', 'out_of_order', 'out_of_service'], + // KB 5.2 chain: vacant_clean → vacant_dirty is the documented "clean room + // found dirty" path — the desk must be able to send a room back to HK. + vacant_clean: ['occupied', 'vacant_dirty', 'out_of_order', 'out_of_service'], vacant_dirty: ['clean', 'out_of_order'], clean: ['inspected', 'vacant_clean', 'out_of_order'], inspected: ['guest_ready', 'out_of_order'],