Skip to content

Leyve Sales Center 프런트엔드 메인테이너 계약

라우트와 구성

  • 라우트: /headquarters/sales/customers
  • View: src/views/headquarters/sales/IndexView.vue
  • Page: src/components/headquarters/sales/SalesCustomerAccountsPag.vue
  • 생성 Sheet: src/components/headquarters/sales/SheetCreateSalesCustomerArt.vue
  • 상태: src/composables/useSalesCustomerAccounts.js
  • repository seam: src/composables/salesCustomerAccountRepo.js

본사 Sales Center는 현재 Console과 인증·배포 경계가 같으므로 Console 안에 둔다. 외부 파트너 전용 Partner Center와 화면 저장소는 분리하지만 두 화면이 고객 데이터를 복사하지 않고 동일 backend RPC를 소비한다.

데이터 흐름

  1. composable이 seller 목록을 읽고 전역관리 가능한 first_party 판매주체를 기본 선택한다.
  2. 목록은 선택 판매주체, 검색어와 lifecycle 상태를 list_sales_customer_accounts_v2에 전달한다.
  3. 생성 Sheet의 실제 유입 경로는 판매주체와 독립된 공통 선택지다.
  4. 본사 직원이 폼에서 만드는 고객은 first_party_assisted, 파트너가 만드는 고객은 channel_partner_assisted로 파생한다. 고객 직접가입은 별도 webhook/adapter가 self_service로 기록한다.
  5. 고객계정 생성과 선택적 프로비저닝 요청은 서로 다른 idempotency key를 사용한다.
  6. 실패 후 같은 입력으로 재시도할 때는 완료될 때까지 같은 command key를 재사용한다.
  7. sales_customer_events의 INSERT를 Realtime invalidation으로 구독한다.
  8. 이벤트 payload로 목록 행을 직접 만들거나 수정하지 않고 list_sales_customer_accounts_v2를 다시 호출한다.
  9. get_my_sales_management_capabilities_v1canApprovePartners가 참일 때만 신청을 읽고, 검토 메모 확인 다이얼로그에서 approve_partner_application_v1을 호출한다.
  10. canOperateProvisioning이 참일 때만 list_sales_provisioning_operations_v1을 읽고 프로비저닝 운영 카드를 렌더링한다.
  11. 수동 재시도 확인 다이얼로그는 선택 행의 kind, queueItemId, 현재 attempts와 필수 사유를 retry_sales_provisioning_operation_v1에 전달한다.
  12. 성공 뒤 고객 목록·운영 목록·필요 시 파트너 신청 목록을 다시 읽는다. stale/busy 오류는 로컬 상태로 덮어쓰지 않고 새로고침을 안내한다.

Supabase 설정이 없으면 prototype repository를 사용한다. 이 저장소는 UI·업무 흐름 검증용이며 운영 동시성·다중 브라우저 영속을 제공하지 않는다.

판매주체 UI 규칙

  • 본사 직판·직접가입: first_party 판매주체
  • 파트너 판매·서비스: 실제 channel_partner 판매주체
  • 본사 전역 사용자는 파트너를 대신해 생성할 수 있지만, 귀속은 선택한 실제 파트너로 기록한다.
  • 본사용 파트너 옵션을 추가하지 않는다.
  • 최초 귀속과 현재 담당배정을 같은 Select로 덮어쓰지 않는다.

Partner Center 연결

Partner Center adapter는 로그인 세션에서 seller organization을 고정한다. 사용자가 임의의 판매주체 ID를 입력하지 않게 하고, backend membership/RLS가 다시 검증한다. 본사 화면만 전역관리 권한으로 seller 범위를 전환할 수 있다.

파트너 승인 뒤 worker가 channel_partner 판매주체와 최초 admin membership을 만들면 get_my_sales_seller_context_v1의 결과에 나타난다. 승인 화면은 seller ID를 생성하거나 추측하지 않는다.

Sales Center의 파트너 신청 카드는 backend capability canApprovePartners가 참인 경우에만 렌더링한다. seller 목록의 canManageAll은 조회 범위 표시에 쓰일 수 있지만 승인 권한 근거가 아니다. 승인 버튼은 submitted | under_review 행에만 표시하고 검토 메모가 비어 있으면 확인을 비활성화한다. 최종 권한·상태·공백 메모 검증은 approve_partner_application_v1이 담당한다.

프로비저닝·Realtime 경계

  • 브라우저는 request_sales_customer_provisioning_v1까지만 호출한다. Company/Auth/Office 생성 RPC와 Edge worker는 service_role 전용이다.
  • 기존·신규 Auth 사용자의 Company·Office membership은 모두 처음에 invited다. /auth/company-selectlist_my_pending_sales_company_invitations_v1로 자기 초대를 표시하고 사용자가 확인 다이얼로그에서 accept_sales_company_invitation_v1을 직접 호출해야 활성화된다.
  • pending 초대는 활성 Company가 0·1·여러 개인 경우 모두 표시한다. 초대가 있으면 단일 Company 자동 진입을 억제해 초대를 숨기지 않는다. 수락 RPC 중에는 ESC·backdrop·닫기 버튼으로 다이얼로그를 닫을 수 없다.
  • 목록의 고객 lifecycle active프로비저닝 완료, administratorInvitationStatus관리자 수락 대기/완료로 분리해 표시한다.
  • Realtime 채널은 public.sales_customer_events, event는 INSERT만 사용한다. RLS가 현재 seller가 읽을 수 있는 고객 이벤트만 허용한다.
  • 이벤트 callback은 payload를 폐기하고 composable의 현재 seller·검색어·상태 필터로 전체 목록을 다시 조회한다. 같은 microtask 안의 연속 invalidation은 한 번으로 합친다.
  • component unmount에서 먼저 disposed 상태를 세우고 채널을 제거한다. async 초기화 완료 뒤 subscribe하거나 microtask에 대기 중인 reload도 disposed면 폐기한다. injected/prototype repository에 Realtime API가 없으면 안전한 no-op이다. Realtime은 전달 보장 원장이 아니므로 재진입 시 초기 조회가 정본을 복구한다.
  • 운영 목록은 customer event Realtime invalidation 때 고객 목록과 함께 재조회한다. partner outbox 자체는 private이므로 이벤트가 없거나 연결이 끊긴 경우 카드의 새로고침과 페이지 재진입이 정본을 복구한다.
  • 운영 상태 라벨은 SALES_PROVISIONING_OPERATION_STATES, 유형 라벨은 SALES_PROVISIONING_OPERATION_KINDS, DTO 정규화는 normalizeSalesProvisioningOperation 한곳에서 관리한다.
  • prototype repository는 실패 customer와 10회 소진 partner 예시를 제공하고, 수동 재시도 시 attempts 0·pending·감사 횟수 증가를 모사한다. 운영 동시성의 실제 보장은 SQL RPC 테스트가 담당한다.

화면을 공유해야 한다면 page 전체를 복사하지 말고 repository DTO, 상태 라벨과 폼 schema를 공통 package로 승격한다. Console/Center의 app shell과 권한 피드백은 각 표면에 남긴다.

제품 표면 이름

  • console: 다제품·조직 통제면
  • center: Partner/Sales처럼 특정 직무의 집중 업무면
  • portal: 개인 셀프서비스
  • kiosk: 현장 공용 단말

저장소 물리 rename은 이번 구현 범위가 아니다. 명명 결정은 docs/decisions/SURFACE-REPOSITORY-NAMING-2026-07-19.md를 따른다.

테스트

  • src/composables/__tests__/salesCustomerAccountRepo.spec.js
  • src/composables/__tests__/useSalesCustomerAccounts.spec.js
  • src/composables/__tests__/salesDistributionCoreMigration.spec.js
  • src/composables/__tests__/salesProvisioningWorker.spec.js
  • src/composables/__tests__/salesProvisioningOperationsMigration.spec.js
  • src/composables/__tests__/companySignupRepo.spec.js
  • src/router/__tests__/salesCenterRoute.spec.js
  • supabase/tests/sales_distribution_core_smoke.sql
  • supabase/tests/sales_partner_provisioning_worker_smoke.sql