Skip to content

관리비 청구 대상 명부 (BE 참고 매뉴얼)

범위와 상태

관리비관리 단독 상품의 property_space × payer 관계를 저장하고 실제 부과·청구 subject snapshot으로 연결한다. 임대차·숙박 점유 원장을 조회하거나 FK로 요구하지 않는다. Migration 20260719125225_service_charge_subject_roster.sql. ✅구현

데이터 모델

테이블역할
service_charge_subject_rostersCompany·Office당 optimistic revision 1개
service_charge_subject_roster_entries활성 유닛당 현재 청구 대상 1개
private.service_charge_subject_roster_commands요청 payload·응답 exact replay와 거절 이력

entry는 다음 UUID 범위 FK를 동시에 만족해야 한다.

  • (property_space_id, company_id, office_id)property_spaces
  • (member_assignment_id, office_id, member_party_id)office_party_assignments
  • (member_party_id, company_id)master_parties

contract_management_code는 최초 연결 시 SC- + 공간 UUID/배정 UUID의 deterministic hash로 자동 생성한다. 같은 공간·같은 배정을 다시 저장하면 기존 코드를 유지한다. 고객 목록에는 노출하지 않는 관리코드다.

RPC 계약

get_service_charge_subject_roster(company_id, office_id)

반환값:

  • revision
  • entries[]: 공간 UUID, 유닛 등록코드/이름, 계약 관리코드, 당사자 UUID, Office 배정 UUID, 대상명·등록번호
  • candidates[]: 활성 member 또는 ar_customer 역할 대상
  • summary.unitCount, summary.assignedCount

인증, 관리비 entitlement, Office 읽기 권한을 모두 요구한다.

replace_service_charge_subject_roster(company_id, office_id, expected_revision, rows, request_key)

브라우저의 유일한 mutation gateway다. rows[]propertySpaceId, memberPartyId, memberAssignmentId만 허용한다. 서버가 다음을 한 트랜잭션에서 검증한다.

  1. Company·Office와 관리비 entitlement
  2. Office 관리 권한
  3. 요청 키 형식과 exact replay
  4. expected revision
  5. 활성 관리비 unit profile 소속 공간
  6. 같은 Office의 활성 party/assignment 및 member|ar_customer 역할
  7. 공간 중복 없음
  8. 모든 활성 관리비 유닛 지정

검증 오류는 status=rejectederrors[]로 명령 ledger에 남는다. 성공은 기존 entry 집합을 전부 교체하고 revision을 정확히 1 올린다. 같은 request key·같은 payload는 원래 응답을 반환하고 다른 payload는 service_charge_subject_roster_idempotency_conflict다.

보안

  • 두 public 테이블 모두 RLS enabled
  • authenticated/service_role은 public 테이블 SELECT만 명시 grant
  • authenticated mutation은 public RPC execute만 허용
  • public/anon의 함수 실행과 모든 직접 DML을 revoke
  • definer 함수는 search_path = ''
  • RLS는 관리비 entitlement와 private.can_read_service_charge_office를 함께 확인

부과·청구 불변식

프런트는 roster entry를 useAllocations의 member/contract/allocation 원자로 변환한다. 부과확정 payload의 각 allocation subject는 다음을 동결한다.

text
service-charge-subject.v1
contractManagementCode
propertySpaceId
memberPartyId
memberAssignmentId

서버의 기존 subject projection 검증과 invoice v3 검증이 현재 Company·Office의 공간·당사자·배정·역할 및 부과 Fact 금액을 재검증한다. 따라서 roster는 편집 가능한 현재 설정이고, charging/invoice facts가 과거 관계 SSOT다.

테스트와 운영 확인

  • SQL: supabase/tests/service_charge_subject_roster_smoke.sql
  • migration contract: serviceChargeSubjectRosterMigration.spec.js
  • client exact RPC: serviceChargeSubjectRosterRepo.spec.js
  • 실제 allocation → charging subject → invoice v3: serviceChargeOperationalSubjectE2e.spec.js

SQL smoke는 로컬 disposable DB에서만 실행하며 항상 rollback한다.