다크모드
Party Directory / 거래처 마스터 — BE 참고
독자: BE 개발자/AI. 구현 정본은
20260716232824_party_directory_foundation.sql이다.
1. 이번 슬라이스
Wave 1 F1-A 첫 운영 슬라이스는 거래처 화면에 필요한 공통 Party 기반만 만든다.
- 포함: Party DB, Office assignment, roles, RLS, create/update RPC, 거래처 CRUD
- 제외: 멤버·사업체 페이지 DB 전환, 계약·Unit, 회계 FK, 안전한 통합/삭제
- 운영 DB 적용: 이 작업에서는 금지. migration 파일과 로컬 검증 계약만 제공한다.
2. Identity 불변식
| 구분 | 저장 위치 | 규칙 |
|---|---|---|
| 시스템 고유코드 | master_parties.id | UUID PK, 고객에게 노출하지 않음 |
| 관리코드 | office_party_assignments.management_code | Office 범위 unique, create 시 입력 또는 자동 생성, 이후 불변 |
| 등록종류·등록번호 | master_parties.registration_kind/registration_number | 중복 허용, create 이후 불변 |
| 이름 | master_parties.display_name | 중복·수정 허용 |
등록번호는 identity가 아니다. 같은 등록번호가 여러 UUID에 존재할 수 있고 자동 병합하지 않는다. 조회·중복 후보 표시에만 사용한다.
3. 스키마
master_parties
Company 범위 Party 본체다. party_kind, legal_type, 등록정보, 이름, 대표자, 사업·연락 속성, 상태와 작성자를 가진다.
company_id → companies.id(company_id, registration_kind, registration_number)는 비고유 조회 인덱스company_id, 등록종류, 등록번호 변경은 trigger가party_identity_immutable로 거부
office_party_assignments
Party를 Office에 노출하고 고객 안정키인 관리코드를 부여한다.
office_id → offices.idparty_id → master_parties.idunique(office_id, management_code)unique(office_id, party_id)- Office와 Party의 Company가 다르면
party_office_company_mismatch - office, party, management code 변경은
party_assignment_identity_immutable
office_party_roles
assignment별 다중 역할이다.
memberar_customerap_vendorfacility_partner
PK는 (assignment_id, role)이다.
4. 권한·RLS
세 public 테이블 모두 RLS를 활성화한다.
- Company active membership: 해당 Company Party/assignment 조회
- Office active membership: 자신이 배정된 Office assignment와 연결 Party 조회
- 수정 가능 Company role:
owner,contract_admin,company_admin - 수정 가능 Office role:
office_admin,operator,accountant viewer,auditor: 읽기만 가능- anon: 테이블·RPC 권한 없음
권한 helper와 mutation 구현은 비노출 private schema의 SECURITY DEFINER, search_path='' 함수다. public RPC는 SECURITY INVOKER wrapper이며 authenticated만 실행할 수 있다.
현재 Supabase Data API는 grant와 RLS를 별도 계층으로 취급하므로 migration에서 둘을 함께 명시한다. authenticated는 테이블 SELECT만 받고 INSERT/UPDATE/DELETE는 받지 않는다. 쓰기는 RPC만 허용한다.
- Supabase: Securing your API
- Supabase: Row Level Security
- Supabase breaking change: tables not exposed automatically
5. RPC
create_master_party
한 transaction에서 다음을 수행한다.
- 인증과 Office 관리권한 확인
- Office Company 결정
- Party UUID 생성
- 관리코드 입력값 정규화 또는
CP-{UUID 8자리}생성 - Party, assignment, roles 삽입
- FE용 JSON 반환
등록번호에는 unique 검사를 하지 않는다. 관리코드 충돌만 DB unique constraint로 거부한다.
update_master_party
assignment_id로 대상과 권한을 잠그고 이름·역할·연락 속성·상태만 갱신한다. 파라미터에 등록정보·관리코드·Company·Office가 존재하지 않는다. DB trigger도 우회 변경을 거부한다.
6. 인덱스
master_parties(company_id)master_parties(company_id, registration_kind, registration_number)— non-uniqueoffice_party_assignments(party_id)office_party_assignments(office_id, status, created_at desc, id)office_party_roles(role, assignment_id)- 작성자 FK partial indexes
7. 검증
partyDirectoryMigration.spec.js: 테이블·불변 trigger·RLS·grants·private helper·인덱스 정적 계약partyDirectoryRepo.spec.js: query/RPC payload와 DB↔FE 정규화- 실제 SQL reset/advisor는 Docker 또는 Supabase dev branch에서 migration 적용 전 반드시 실행
- 운영 DB에는 이 작업에서 적용하지 않는다.
8. 후속 계약
- 멤버와 사업체를 같은 Party UUID에 연결
- 시설 projection을 DB-backed adapter로 전환
- 회계 전표에는
party_idFK와 거래 당시 명칭 snapshot을 함께 저장 - Unit 정본 이후 occupancy contract가
office_party_assignment_id를 참조 - 통합은 모든 FK 재연결과 감사 이력 RPC가 생기기 전까지 금지