다크모드
호스피탈리티 룸 타입 백엔드 계약
As-built
정본 migration은 20260718180000_hospitality_room_type_master.sql이다. 룸 타입은 Hospitality 상품이 소유하는 Office 범위 마스터다. 시설·임대차·회계 상품 객체를 참조하지 않고, 물리 룸과의 경계에서는 플랫폼 코어 property_space 분류만 검증한다.
hospitality_room_types의 필드는 다음처럼 나뉜다.
- 고정 identity: UUID, Company·Office,
management_code,registration_code,space_category - 가변 속성:
display_name,bed_configuration, 기준·최대 정원, 선택reference_base_rate,currency,active|inactive - 동시성·감사:
revision, actor, created/updated timestamp
관리코드는 비우면 서버가 생성하며 생성 요청에서만 지정할 수 있다. 룸 타입 등록코드는 같은 Company·Office에서 고유하다. 삭제 RPC는 없고 사용 종료는 inactive로 표현한다.
RPC와 권한
list_hospitality_room_types_v1(company, office, status?)create_hospitality_room_type_v1(...)update_hospitality_room_type_v1(..., expected_revision, ...)
public wrapper는 security invoker, 빈 search_path이며 authenticated/service role에만 execute를 준다. private definer 구현은 auth.uid(), Hospitality entitlement, Company·Office read/manage 권한을 다시 검사한다. exposed table은 RLS를 켜고 authenticated에는 SELECT만 부여한다. direct INSERT·UPDATE·DELETE는 회수했다.
private.hospitality_room_type_commands는 (company, command, request_key) 멱등 원장이다. 같은 payload 재시도는 기존 응답을 반환하고 다른 payload 재사용은 hospitality_room_type_request_key_conflict다. hospitality_room_type_events는 create/update 전후 payload와 revision을 남기는 append-only 감사 원장이다.
룸·예약 불변식
hospitality_rooms(company_id, office_id, room_type_code)는 같은 scope의 타입 등록코드를 FK로 참조한다. 기존 opaque code를 무리하게 변환하지 않도록 constraint는 NOT VALID로 추가했지만 신규 INSERT와 code 변경에는 즉시 적용된다.
배정 trigger는 다음을 강제한다.
- 신규 룸·룸 타입 변경: same-office active type
- 룸:
room_type.space_category = property_space.space_type - 신규 예약·예약 타입 변경: same-office active type
- 타입이 inactive가 되어도 기존 룸·예약 row는 보존하며, 룸의 동일 code 유지 업데이트도 허용
예약의 room_type_code, estimated_amount와 folio 금액은 거래 시점 snapshot이다. reference_base_rate를 수정해도 기존 예약·folio를 다시 계산하거나 갱신하지 않는다.
기존 code preflight
기존 hospitality_rooms와 hospitality_reservations에는 code 외에 이름·침대·정원 메타데이터가 없으므로 migration이 임의 타입이나 demo row를 만들지 않는다. service-role-only private.hospitality_room_type_backfill_preflight_v1()은 실제 사용 중이지만 아직 등록되지 않은 code, 룸·예약 건수와 관찰된 공간 분류만 반환한다.
운영 적용 절차는 preflight 확인 → 담당자가 실제 명칭·침대·정원과 단일 공간 분류를 확인 → 해당 Office에 타입 생성 → preflight 0건 확인 → hospitality_rooms_room_type_scope_fkey validate 순서다. 공간 분류가 둘 이상 관찰되면 자동 추론하지 않고 데이터 정정 결정을 먼저 내린다.
제품 경계와 검증
참고 기준 요금은 Hospitality 내부 참고값일 뿐 요금 subledger, 회계 전기 또는 전자문서 adapter가 아니다. 향후 시즌·일자 요금은 Hospitality-owned subledger로 추가하고 외부 회계 연결은 optional adapter로 분리한다.
- 실제 PostgreSQL rollback:
hospitality_room_type_master_smoke.sql - 기존 회귀:
hospitality_room_master_smoke.sql,hospitality_reservation_room_allocation_smoke.sql,hospitality_room_operations_smoke.sql,hospitality_financial_ledgers_smoke.sql - 구조·repo 계약:
hospitalityRoomTypeMigration.spec.js,hospitalityRoomTypeRepo.spec.js
smoke는 manager 성공, 무권한·미구매·cross-office 차단, 중복·unknown·inactive code, category 불일치, identity·revision, request replay/conflict, direct write·event mutation 거부, 기존 룸 보존과 예약 금액 snapshot 불변을 검증하고 rollback한다.