다크모드
BE handoff — 상품 계약·entitlement·provisioning
구현 도메인 (Supabase prototype)
product_catalog: 판매 가능한 모듈과 서버 가격product_plans/product_plan_items: Goldnus 관리업무, 일반 ERP 프리셋organization_product_contracts/organization_product_contract_items: Organization 계약, 결제 주기, 가격 snapshot, 수량, 효력일product_checkout_sessions/product_checkout_items: Organization·Workspace 범위의 견적 snapshot, 변경 action, idempotency keyworkspace_module_entitlements: 계약 상품을 실제 Workspace에 활성화한 module key와 유효기간workspace_effective_module_entitlements(organization_id, workspace_id): 활성 Organization·Workspace·catalog·계약·계약 항목과 유효기간을 교차 검증한 좁은 SECURITY DEFINER runtime read RPCorganization_effective_product_contract_items(organization_id): Organization 계약의 현재 유효 상품 집합을 반환하는 runtime read RPCproduct_provisioning_jobs: 결제 승인 이후 앱 초기화 상태
제품 표시명과 분류의 정본은 src/composables/productVocabulary.js와 docs/decisions/PRODUCT-NAMING-TAXONOMY-2026-07-26.md다. PRODUCT_CATEGORIES는 도메인, PRODUCT_GROUPS는 프로퍼티 도메인의 거래·분양과 운영 제품군을 표현한다. 실행 제품의 module_key는 route, 코드와 DB aggregate가 공유하는 bounded-context identifier다.
마이그레이션 정본은 supabase/migrations/20260822000000_organization_workspace_canonical_baseline.sql이다. 이전 migration version과 원격 적용 기록은 Git/운영 이력일 뿐 현재 DB 재생 경로가 아니다. 원격 Prototype reset·재배포는 별도 승인을 받아 수행한다.
상태와 책임
draft → configured → checkout_pending → payment_authorized → provisioning → active
- 견적 생성 시 서버가 가격·VAT·수량을 다시 계산한다.
- 결제 webhook은 서명 검증 후 idempotent하게 승인 상태를 기록한다.
- provisioning 성공 후에만 entitlement를 활성화한다.
- 프론트가 보낸 합계나
active상태를 신뢰하지 않는다. - 상품 제거는 계약 정책에 따라 제거 시작일
ends_at을 갖는 예약 변경으로 저장한다. - 추가·제거가 함께 요청되면 동일 change request에 담되 entitlement 효력 시점은 item별로 기록한다.
payment_failed에서는 provisioning job을 만들지 않고,payment_authorized이후에만 idempotent job을 생성한다.provisioning지연 중 기존 entitlement를 철회하지 않는다.
Supabase RPC
begin_product_checkout(organization_id, workspace_id, ...) → checkout uuid: Organization 관리 권한과 Workspace 소속 확인, 카탈로그 유효성 검사, 가격/VAT 서버 재계산, snapshot 저장confirm_product_checkout(checkout_id, outcome) → status: 결제 실패 또는 provisioning 시작complete_product_provisioning(checkout_id) → active: Organization 계약 항목과add대상 Workspace entitlement를 idempotent하게 활성화
상품 변경의 desired set은 Organization 계약 기준이다. add만 checkout 대상 Workspace에 자동 배정하며 keep은 기존 Workspace 배정을 바꾸지 않는다. 다른 활성 Workspace가 사용 중인 module 제거는 module_in_use_by_other_workspace로 거부하고, 해당 Workspace 배정을 먼저 해제해야 한다. 제거 시작일인 ends_at·valid_until은 exclusive 경계라 해당 날짜부터 사용할 수 없다.
신규 Organization FE는 저장된 결제수단 조회가 연결되기 전까지 payment_method=invoice만 보낸다. RPC의 registered-card 값은 향후 실제 카드 등록·조회 계약을 연결할 때 사용하며, 카드 별칭을 프론트에서 임의 생성하지 않는다.
별도 사용자 소유 상품 Workspace나 goldnus 호환 slug는 없다. UI와 checkout은 현재 로그인한 Organization과 실제 Workspace UUID를 명시적으로 사용한다. 실제 결제수단·결제내역 API가 생기기 전에는 레거시 샘플 카드·계약·결제 행을 API 응답처럼 노출하지 않는다.
catalog는 anon/authenticated 읽기 전용이다. 계약과 Workspace entitlement는 활성 Organization·Workspace membership 범위에서만 읽으며 write는 security-definer RPC만 허용한다. mutation RPC는 search_path = public, pg_temp, effective read RPC는 search_path = ''로 고정하고 호출자의 Organization 관리 권한 또는 명시적 Organization·Workspace membership을 다시 확인한다. checkout snapshot은 시작한 사용자만 읽을 수 있지만 계약 소유 주체는 사용자가 아니라 Organization이다.
canonical 검증 기준
product_workspaces,product_subscriptions,product_subscription_items,product_entitlementsrelation은 0개여야 한다.- checkout session은
organization_id와 실제workspaces.id를 함께 보존한다. - 정상 완료 transaction smoke는 Organization 계약 item, 대상 Workspace entitlement, succeeded job을 함께 생성한다.
- 지연 provisioning은 기존 entitlement를 유지하고, 실패 checkout은 계약·entitlement를 변경하지 않는다.
Goldnus ERP와 Leyvecloud ERP는 독립 권한 키가 아니라 plan preset이다. 기능 접근 판단은 반드시 service-charge, accounting, facility 같은 안정적인 module key를 사용한다.
검수 계정 노출 프로필 (2026-07-19)
상품 판매 상태인 lifecycle과 기술 구현 준비도인 implementationStage를 분리한다. 프런트 내부 catalog가 허용하는 준비도는 server-core | partial-server | prototype | planned이며 공개 catalog projection에는 이 내부 필드를 내보내지 않는다.
goldnus@goldnus.com또는 Goldnus Organization Owner:server-core상품 전체를 메뉴·홈·상품 선택에 노출한다.- Goldnus 직원:
server-core필터를 먼저 적용하고 기존 Workspace entitlement와 교집합만 노출한다. leysys@leysys.com또는 Leysys Organization: 미완성 상품과 catalog 밖 preview 앱까지 개발 검수 목적으로 노출한다.- 그 외 계정: 기존 Organization 계약과 Workspace entitlement를 따른다.
현재 partial-server/prototype으로 분류한 상품은 purchasing, asset-management, accounting-source-documents, sales, long-term-repair-planning이다. 나머지 catalog 상품은 server-core다. 이 분류는 DB migration 존재 여부만으로 정하지 않고, 인증된 서버 저장·상태전이·권한 경계까지 고객이 검수 가능한지를 기준으로 갱신한다.
제품명과 안정 키
sales→ 판매·매출채권 / Sales & Accounts Receivablepurchasing→ 구매·매입채무 / Procurement & Accounts Payableaccounting-source-documents→ 회계증빙관리 / Source Document Management (finance-assets)asset-management→ 고정자산관리 / Fixed Asset Managementtax-invoice→ 전자문서 발행 / Electronic Document Issuancehuman-capital→ 인사관리 / Human Resources Management (HRM)real-estate-brokerage→ 매물·거래 / Property Listings & Transactionsproperty-sales→ 분양관리 / Property Sales Managementelectronic-records-management→ 전자기록관리 / Electronic Records Managementsupporting-documents→ 업무증빙관리 / Supporting Document Management
후보 제품 crm, real-estate-brokerage, property-sales, supporting-documents, business-messaging, electronic-records-management은 네이밍 어휘에 존재해도 서버 catalog나 entitlement에 자동 등록하지 않는다. 실제 상품화는 owner 원장, route, 서버 capability, 주문 정책과 검증을 한 변경에서 추가한다.
회계증빙관리의 DB aggregate는 accounting_source_documents, 하위 객체 prefix는 accounting_source_document_*다. 업무증빙관리의 예정 aggregate는 supporting_documents다. 파일 저장 공통 capability는 document_blobs, document_versions, document_links를 사용하며 상품 entitlement로 만들지 않는다. 상세 계약은 docs/decisions/DOCUMENT-DOMAIN-BOUNDARIES-2026-07-26.md가 정본이다.
이 프로필은 프런트 출시 검수 정책이다. 서버의 Organization·Workspace entitlement, RLS와 RPC 권한 검사를 넓히지 않는다. 클라이언트가 보낸 implementationStage, email 또는 노출 프로필을 서버 권한 근거로 신뢰하면 안 된다. 숨겨진 기존 entitlement는 상품 변경 checkout의 desired module set에서 보존해 UI 필터가 암묵적 해지를 만들지 않게 한다.
계약과 Billing Core 경계 (2026-07-26 개정)
contract-management는 독립 entitlement다.contract-billing은 상품 키로 사용하지 않는다. 새 선택·계약·Workspace entitlement를 만들지 않으며contract-to-cashpreset은contract-management와sales를 조합한다.- 후속 migration은 과거
product_catalog.contract-billing행을 비활성화한다. 기존 계약·entitlement snapshot과contract_billing_*데이터는 감사·전환을 위해 보존하지만 capability 판정에는 사용할 수 없다. - 과거
/contract-billing/*북마크 redirect는 FE 호환 정책이다. 서버는 이 URL을 entitlement나 Billing Core 권한 키로 해석하지 않는다. - Billing Core는 청구계획→회차별 채권→미수→수납 충당→반전의 상태·계산·이벤트 계약을 제공할 뿐 별도 entitlement를 요구하지 않는다.
- 판매·매출채권, 관리비관리, 임대차관리는 공통 capability 골격을 재사용하되 product-owned 원장과 RPC를 합치지 않는다. 세부 경계는
docs/decisions/BILLING-FAMILY-BOUNDED-CONTEXTS-2026-07-19.md가 정본이다.
FE 라우팅 경계 (2026-07-13)
기존 고객의 /onboarding/products?mode=manage는 administration의 계정·계약 셸 안에서 렌더한다. URL·RPC·entitlement·provisioning 상태 계약은 변경하지 않았고, 부모 레이아웃만 보존한다. 신규 가입 흐름도 같은 URL을 사용할 수 있으므로 서버는 mode 쿼리를 권한 근거로 신뢰하지 않는다.
상품 선택 표면을 EDS radio-card·checkbox-card로 정정한 작업은 표시와 접근성만 바꾼다. bundleKey, module key 배열, 수량, 견적·결제 RPC 계약은 변경하지 않는다.
상품 구성 단계의 요약 카드는 클라이언트 lineItems로 계산한 VAT 포함 월 예상 이용료를 수량 입력과 함께 즉시 표시한다. 이는 의사결정용 예상치이며 결제 확정값이 아니다. begin_product_checkout은 명시적인 Organization·Workspace UUID와 module key, quantities.units·quantities.users를 받아 카탈로그 가격과 VAT를 서버에서 다시 계산하고, 이후 화면과 결제 처리는 서버 snapshot을 정본으로 사용해야 한다.