다크모드
시설 자산 점검 백엔드 계약
목적과 범위
이 문서는 시설 자산과 점검 lifecycle의 as-built 계약입니다. 기존 facility-owned facility_work_orders와 플랫폼 property_spaces, property_space_aliases, auth/Office/entitlement 계약만 소비합니다. 인사 상품 객체는 최종 점검 실행 계약의 의존성이 아닙니다.
설비 운영 현황판은 이 원장을 변경하지 않고 Office 범위 read model로 소비합니다. 자산을 기준행으로 유지하고 점검 조회 실패를 독립 상태로 다루는 합성 계약은 facility-equipment-operations-board.md에 있습니다.
항목 결과·독립 실행 forward 계약은 supabase/migrations/20260717124056_facility_inspection_checklist_results.sql, exact corrective 폐쇄루프는 supabase/migrations/20260718230000_facility_inspection_work_order_closure_loop.sql, 결과 차수 링크 정체성 보정은 supabase/migrations/20260718270000_facility_inspection_result_link_identity.sql입니다. 실행형 검증은 기존 smoke와 supabase/tests/facility_inspection_work_order_closure_loop_smoke.sql입니다.
데이터 모델
public.facility_assets
- 범위:
company_id + office_id - 고객 비노출 안정키: Company 범위 unique
management_code. 생성 시 선택 입력할 수 있으며 빈 값이면 서버가 생성하고 이후 immutable입니다. - 고객 노출 코드: 선택 가능한
registration_code - 대상:
subject_kind(equipment|unit) + subject_ref - 수정 가능 profile:
display_name만update_facility_asset_profile로 변경합니다. 관리코드·등록코드·대상은 이 RPC가 받지 않습니다. - 공간 자산:
property_space_id를 추가로 저장하고 canonical unit/guest-room 공간에 연결 - 상태:
operational|attention|out_of_service|retired - 낙관적 동시성:
revision bigint
공간 자산은 namespace 없는 관리코드 또는 namespace가 있는 property_space_aliases를 정확히 해석합니다. 동일 canonical 공간에는 자산을 하나만 둘 수 있습니다. 설비는 외부 대상 참조 경계를 유지합니다.
public.facility_inspections
자산, 점검 코드·제목, 계획 시점의 immutable 체크리스트 snapshot, 예정 시각, 선택적인 auth actor/party snapshot, 상태, 현재 결과 projection, revision을 저장합니다. employee/assignment FK와 필수값은 forward migration에서 제거했습니다. 미배정 계획이 가능하고 시작 actor는 auth.uid() 감사 기록에 남습니다.
append-only 이력과 연결
facility_asset_status_transitions: 생성, 수동 상태 변경, 점검 결과 파생 변경facility_inspection_transitions:NULL → planned → in_progress → completedfacility_inspection_work_order_links: 완료된 점검과 작업지시의 명시적 corrective link. 실패 링크는source_result_revision과 당시 실패 항목source_itemssnapshot을 보존facility_inspection_result_revisions: 최초 완료와 명시적 정정 revision의 append-only 원본facility_inspection_item_result_events: revision별pass|fail|not_applicable, note, evidence metadataprivate.facility_inspection_commands: 멱등 요청 payload와 응답 snapshot
이력 update/delete와 RPC 외 직접 쓰기는 trigger로 차단합니다.
상태 머신과 불변식
text
planned <-> in_progress -> completed -> correction revision- 모든 snapshot item은
pass|fail|not_applicable결과가 필요합니다. 실패 항목은 3자 이상의 note가 필요합니다. - 항목 하나라도 실패이면 전체
fail, 아니면pass이며 자산 상태는out_of_service|operational로 파생합니다. - 시작 취소는 사유·expected revision·request key가 필요하며
in_progress → planned만 허용합니다. - 정정은 완료 상태에서만 가능하고 새 result revision이 직전 revision을 가리킵니다. 원본 event update/delete는 차단됩니다.
- 점검과 자산 각각의
expected_revision이 현재 값과 같아야 완료됩니다. fail은 기존 미검증 작업지시 또는 같은 transaction에서 생성한 새 시정 작업지시가 필수입니다.- 한 inspection result revision에는 exact corrective link 하나만 허용합니다. 정체성은
(inspection_id, source_result_revision)이며 동일 작업지시를 여러 정정 차수에서 재사용할 수 있습니다. 기존(inspection_id, work_order_id)고유 제약은 제거합니다. 같은 request key는 응답을 재생하고 stale/different request는 inspection revision에서 차단되어 고아 작업을 남기지 않습니다. source_result_revision is null은 legacy non-result link를 뜻합니다. 이를 revision0으로 정규화하지 않습니다.- 시작 시 점검 행뿐 아니라 현재 자산 행도
FOR UPDATE로 잠그고retired를 다시 확인합니다. 계획 뒤 운영 종료된 자산은 시작할 수 없습니다. - 연결 작업지시는 같은 Company/Office와 같은 대상이어야 합니다. 공간 작업지시는 canonical 공간 참조 또는 해당 공간 별칭 참조를 허용합니다.
- 신규 생성은 사용자가 작업명·우선순위를 확인한
complete_facility_inspection_with_corrective_work_order경로만 허용합니다. 센서나 백그라운드가 임의 생성하지 않습니다.
공개 RPC
create_facility_asset: 공간 별칭 해석을 포함한 자산 등록. 마지막 선택 인자p_management_code를 받고 기존 8개 positional 호출도 default 인자로 호환합니다.update_facility_asset_profile:display_name만 expected revision으로 변경하며 동일 요청키 재호출은 저장 응답을 재생합니다.get_facility_inspection_execution_context: Company·Office·상품 entitlement·관리 권한을 검증한 뒤 같은 대상의 미검증 작업지시와 inspection별 exactcorrectiveLinksdrilldown을 반환합니다.transition_facility_asset_status: expected revision과 사유가 필요한 수동 상태 변경plan_facility_inspection: 정규화한 checklist snapshot과 선택 actor snapshot으로 미배정 계획 가능start_facility_inspection: expected revision과 운영 자산을 검증하고 실행 actor 기록cancel_facility_inspection_start: 사유와 expected revision으로 예정 상태 복귀complete_facility_inspection: 항목 결과·점검/자산 revision·실패 작업지시를 원자적으로 저장complete_facility_inspection_with_corrective_work_order: 실패 결과, exact asset target 작업지시 생성, 결과/자산 변경, failed-item link를 한 transaction에서 저장correct_facility_inspection_results: 원본을 유지한 채 새 결과 revision과 정정 사유 저장
모든 public 함수는 SECURITY INVOKER wrapper이며 실제 변경은 private SECURITY DEFINER 구현에서 수행합니다. actor는 입력받지 않고 auth.uid()에서 파생합니다. authenticated에는 공개 테이블 SELECT만 부여하고 직접 INSERT/UPDATE/DELETE를 허용하지 않습니다.
각 변경 RPC는 p_request_key를 사용합니다. 같은 키와 같은 payload는 저장된 응답을 재생하고, 다른 payload면 facility_inspection_idempotency_conflict입니다.
인가와 RLS
모든 신규/교체 RPC는 auth.uid(), 정확한 Office 관리 권한, private.require_office_product(..., 'facility')를 확인합니다. RLS 조회도 Office 권한과 활성 facility entitlement를 함께 요구합니다.
감사
같은 transaction에서 운영 감사 이벤트를 남깁니다.
facility.asset_createdfacility.asset_status_changedfacility.asset_profile_updatedfacility.inspection_plannedfacility.inspection_startedfacility.inspection_start_cancelledfacility.inspection_completedfacility.inspection_results_corrected- 점검으로 자산 상태가 바뀌면 추가
facility.asset_status_changed - 작업지시 연결 시
facility.inspection_work_order_linked
감사 기록이 실패하면 업무 변경도 rollback됩니다.
검증
신규 smoke는 인사 레코드 없이 미배정 계획, 시작·시작 취소·재시작, 항목 누락 차단, 실패 작업지시, 멱등 완료, stale 정정 차단, 원본 보존, append-only trigger, facility entitlement 정지를 검증하고 rollback합니다.