Skip to content

프로퍼티 유닛 관리 — Frontend 핸드오프

진입점과 컴포넌트

정본 화면은 /administration/organization/workspace/property/console/unit이다. /master-data/resource/unit/general/master-data/property/console/unit도 같은 정본 View를 재사용하는 호환 라우트다. 별도 화면·목록·상태를 만들지 않는다.

text
MainOrg.vue
├─ blocks/DataToolBarOrg.vue
│  ├─ #sheet-create-workspace-unit-art
│  ├─ #sheet-import-property-units-art
│  └─ #dialog-archive-workspace-units-art
├─ blocks/DynamicTableOrg.vue
├─ overlays/SheetCreateUnitArt.vue
├─ overlays/SheetCreateUploadUnitArt.vue
└─ overlays/DialogArchiveUnitsArt.vue

관리비 부과산정의 유닛 관리 버튼은 이 라우트로 이동할 뿐 프로퍼티 UI를 import하지 않는다. 관리비 일반·중간정산·공지 화면에는 업로드 sheet mount가 없다. 범용 스캐폴딩 ID인 sheet-create-upload-unit-art를 재사용하지 않는다.

단건 추가·선택 삭제는 상품과 무관한 Workspace 공간 command다. DataToolBarOrg의 CSV 가져오기만 useModuleSubscription().isModuleEnabled('service-charge')를 읽어 관리비 미신청 Workspace에서 disabled 처리한다. 상품 미신청 상태에서도 공용 유닛 목록·현재 활성 Unit 수·단건 관리는 사용할 수 있다.

MainOrg.vue는 플랫폼 코어 화면과 선택 상품 adapter를 조립하는 명시적 composition root다(config/product-boundaries.json). 관리비 parser·command composable은 이 파일에서만 주입하고, core overlay는 parseCsv·importRows·importing props만 소비한다. 따라서 core 하위 컴포넌트가 관리비 구현을 직접 import하지 않으며 다른 상품은 같은 방식으로 선택 연결할 수 있다.

페이지는 active Workspace ID와 관리비 entitlement를 watch해 catalog를 hydrate한다. 관리비 신청 상태면 atomic service-charge catalog RPC로 profile과 command revision을 함께 읽고, 미신청 상태면 상품 table에 의존하지 않는 core profile read를 사용한다. 단건 생성·archive 후에는 catalog와 Workspace unitCount projection만 다시 hydrate한다.

hydrate 중·실패 상태에서는 쓰기 command를 잠그고, 오류 안내의 다시 불러오기로 복구한다. Workspace가 바뀌면 선택된 Unit ID를 초기화한다.

Unit Seat와 Unit UI 계약

  • 상단 요약은 현재 활성 Unit 수만 표시한다.
  • Workspace 화면은 purchasedQuantity, remainingQuantity, subscriptionStatus, 계약 revision, unitPrice, 통화, 과금주기를 요청하거나 표시하지 않는다.
  • 추가·복원 한도는 DB가 강제한다. workspace_unit_capacity_exceeded이면 계약 상세 없이 Organization Owner에게 문의하도록 안내한다.
  • 목록 선택 키는 legacy unitCode가 아니라 propertySpaceId다.
  • 삭제 확인 후 archive_workspace_units를 호출한다. 물리 delete UI나 API는 없다.
  • archive 성공 후 선택을 비우고 목록을 다시 읽는다.
  • 단건 추가는 create_workspace_unit을 호출한다. 동·층·호, 등록코드, 용도, 3개 원천 면적만 입력하고 공급·계약면적은 파생 표시한다.

데이터 흐름

text
File.text()
  → parseServiceChargeUnitCsv(text)
  → 오류 0건일 때 preview + requestKey 고정
  → useServiceChargeUnitImport().importRows({ rows, requestKey, expectedRevision })
  → RPC committed
  → core catalog 재hydrate
  → propertyUnitMaster reactive catalog 교체
  → 유닛 목록·배분·첫 부과 readiness 갱신

useServiceChargeUnitImport()는 active Organization·Workspace를 내부에서 가져온다. hydrate()importRows()는 caller가 actor/provider/service-role을 넘기지 못하게 한다. import 성공 시 latest catalog와 revision을 재조회하고 activeWorkspace.unitCount projection을 갱신한다. 실패나 rejected 결과에서는 기존 cache를 유지한다.

useWorkspaceUnitSeatManagement()도 scope와 actor를 UI 입력으로 받지 않는다. active Organization·Workspace에서 scope를 유도하고 다음 repo command만 노출한다.

text
createUnit()   → create_workspace_unit
archiveUnits() → archive_workspace_units

CSV·상태 계약

  • exact header: 등록코드,동명,층명,호명,용도,전용면적,주거공용면적,기타공용면적,공급면적,계약면적
  • UTF-8/BOM, quoted comma, escaped quote 지원
  • .csv, 5MB, 5,000행 한도
  • formula prefix, 잘못된 숫자·필수값·헤더·면적 합계는 all-or-nothing 오류
  • preview는 최초 10행만 표시하고 전체 건수를 별도로 보여 준다.
  • submit 중 close·file change·재submit을 막는다.
  • validation/RPC 실패 시 sheet를 유지한다. committed 뒤에만 close/reset한다.

requestKey는 유효한 파일을 선택했을 때 한 번 만들고 응답을 알 수 없는 통신 실패의 같은 payload 재시도 동안 유지한다. 서버가 명시적으로 rejected를 반환하면 그 key의 결과가 확정됐으므로 폐기한다. 특히 revision conflict는 최신 catalog를 먼저 hydrate한 뒤 새 key로만 다시 제출한다. 파일을 다시 골라도 새 key를 만든다.

UI 규율

시트는 SSOT의 sheet-width-3xl, sheet-lg, field/input, alert, card/table, sheet-footer 패턴만 사용한다. 로컬 style/override는 없다. 목록의 관리코드는 노출하지 않고 등록코드·이름·속성 순서를 유지한다.

단건 Create 시트는 세로 폼 집중 폭 3xl, 선택 삭제는 dialog-md를 사용한다. 현재 활성 Unit 요약은 기존 card·타이포그래피·grid만 사용하며 새 CSS나 제품 로컬 override를 만들지 않는다.

테스트

  • parser edge cases와 row normalization
  • import repo RPC payload/response
  • hydration과 propertyUnitMaster/useAllocations/readiness 반영
  • overlay: 잘못된 파일·preview·double-submit·실패 유지·성공 close
  • scripts/service-charge-product-boundary.spec.js: 관리비 upload wrapper/mount/trigger 0
  • workspaceUnitSeatManagement.spec.js: 현재 사용 수량, 계약정보 비노출, 일반 capacity 초과 오류, 생성, archive
  • workspaceUnitSeatMigration.spec.js: RLS·grant·capacity trigger·RPC 계약
  • UnitPage.spec.js: 두 호환 route가 쓰는 정본 페이지의 추가/삭제/CSV command 배선