Skip to content

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.idUUID PK, 고객에게 노출하지 않음
관리코드office_party_assignments.management_codeOffice 범위 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.id
  • party_id → master_parties.id
  • unique(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별 다중 역할이다.

  • member
  • ar_customer
  • ap_vendor
  • facility_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만 허용한다.

5. RPC

create_master_party

한 transaction에서 다음을 수행한다.

  1. 인증과 Office 관리권한 확인
  2. Office Company 결정
  3. Party UUID 생성
  4. 관리코드 입력값 정규화 또는 CP-{UUID 8자리} 생성
  5. Party, assignment, roles 삽입
  6. 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-unique
  • office_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. 후속 계약

  1. 멤버와 사업체를 같은 Party UUID에 연결
  2. 시설 projection을 DB-backed adapter로 전환
  3. 회계 전표에는 party_id FK와 거래 당시 명칭 snapshot을 함께 저장
  4. Unit 정본 이후 occupancy contract가 office_party_assignment_id를 참조
  5. 통합은 모든 FK 재연결과 감사 이력 RPC가 생기기 전까지 금지