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')를 읽어 관리비 미신청 Office에서 disabled 처리한다. 상품 미신청 상태에서도 공용 유닛 목록·Seat 현황·단건 관리는 사용할 수 있다.

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

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

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

Unit Seat와 Unit UI 계약

  • 상단 요약은 purchasedQuantity, usedQuantity, remainingQuantity를 각각 표시한다.
  • remainingQuantity === 0이면 단건 추가를 비활성화한다.
  • Office 화면은 unitPrice, 통화, 과금주기를 요청하거나 표시하지 않는다.
  • 목록 선택 키는 legacy unitCode가 아니라 propertySpaceId다.
  • 삭제 확인 후 archive_workspace_units를 호출한다. 물리 delete UI나 API는 없다.
  • archive 성공 후 선택을 비우고 목록·capacity를 다시 읽는다.
  • 단건 추가는 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 Company·Office를 내부에서 가져온다. hydrate()importRows()는 caller가 actor/provider/service-role을 넘기지 못하게 한다. import 성공 시 latest catalog와 revision을 재조회하고 activeOffice.unitCount projection을 갱신한다. 실패나 rejected 결과에서는 기존 cache를 유지한다.

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

text
getCapacity()  → get_workspace_unit_seat_capacity
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를 사용한다. Seat 현황은 기존 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 배선