Skip to content

FE 메인테이너 — 설정>항목 (부과항목 마스터 편집기)

독자: FE 메인테이너/AI. "이 페이지를 이어 개발하려면 무엇을 알아야 하나". 대상: 관리비 설정>항목 페이지 — useCharges 캐논의 정의 편집기. 부과 사슬 activeCharges 전파. 구현 상태: ✅구현 (2026-06-29, worktree feat/invoicing-detail-axis-aggregate).


1. 라우트

pathview상태
/service-charge/setting/item/generalsrc/views/service-charge/setting/item/general/IndexView.vue✅구현

설정>항목은 실제·예정 라인이 공유하는 서비스 레벨 설정이다. 라우트 팩토리 = src/composables/billingLines.js + src/router/buildLineRoutes.js.


2. 컴포넌트 트리

IndexView.vue
├── HeaderOrg.vue
└── MainOrg.vue
    ├── SearchToolBarOrg.vue
    ├── CriteriaToolBarOrg.vue
    ├── DataToolBarOrg.vue            (추가 진입·선택삭제)
    ├── DynamicTableOrg.vue           (부과항목 리스트)
    ├── PaginationToolBarOrg.vue

    └── (overlays)
        ├── SheetCuItemArt.vue        (부과항목 추가 시트 — addCharge)
        └── SheetReadItemArt.vue      (부과항목 상세 read↔edit — updateChargeDef)
        └── SheetManageChargeBasisValuesArt.vue (호별 배분값 조회·편집)
    └── ChargeBasisFieldOrg.vue       (배분방식 조건부 폼·계산식 요약)

src/components/service-charge/setting/item/general/ 하위에 전부 위치.


3. 소비 컴포저블

3-1. useCharges — 부과항목 캐논 단일 소스

src/composables/useCharges.js. 모듈 스코프 reactive 싱글톤. 설정>항목과 부과 사슬 전체가 같은 인스턴스를 공유.

노출종류역할
chargesref(Charge[])전체 항목 배열(활성+비활성). demoSeed.buildSeed().charges 초기화.
activeChargescomputedcharges에서 active !== false인 항목만 필터. 부과 사슬 소비처가 이것을 읽는다.
addCharge(def)fn → chargeCode신규 정의 1행 추가(금액 0·active true·chargeCode 생성).
removeCharge(chargeCode)fn항목 제거(영구). charges에서 splice.
updateChargeDef(chargeCode, patch)fnDEF_FIELDS(name/nature/taxCategory/taxInvoiceType/chargeBasis)만 패치. 금액·chargeCode 불변.
toggleActive(chargeCode, on)fncharge.active = on. activeCharges에 즉시 반영.
amountsOf(charge)fn금액 파생(부과 사슬 전용 — 설정>항목 미사용).

3-2. useChargeItemUi — UI 선택 상태 (비즈니스 로직 없음)

src/components/service-charge/setting/item/general/useChargeItemUi.js. 모듈 싱글톤.

노출역할
selectedCoderef — 상세 시트에 열릴 항목 chargeCode
checkedCodesref — 체크박스 다중 선택(삭제 대상)
selectItem(code)행 클릭 시 selectedCode 세팅
toggleCheck(code, on)체크박스 on/off
clearChecked()삭제 후 선택 초기화

DynamicTableOrgSheetReadItemArt·DataToolBarOrg가 공유한다.

useChargeItemUi는 설정 페이지에 한정되지 않는다. 부과산정 표에서도 항목명 클릭 시 selectItem(chargeCode)를 호출한 뒤 같은 SheetReadItemArt를 연다. 라우트가 달라도 선택 키와 상세 컴포넌트를 복제하지 않는다.

3-3. useAllocationBasisValues — 항목별 호 배분값 draft/commit

src/composables/useAllocationBasisValues.js. 항목 정의, allocation line, 검침 지침을 연결하는 모듈 싱글톤이다.

노출역할
ensureChargeLines(chargeCode)현재 계약 원자를 신규 항목의 배분 라인으로 멱등 생성. 생성 시트가 addCharge 직후 호출.
openBasisValues(chargeCode)기준을 읽고 호별 draft를 만든다. 한 호에 점유기간 라인이 여러 개여도 UI 행은 하나.
draftRows / dirty / valuesValid호별 입력 draft와 저장 게이트. 유한한 0 이상 값만 허용.
commitBasisValues()사용량은 useMeterReadings, 지분은 동일 호의 모든 allocation line에 반영.
summaryFor(chargeCode)상세 시트의 대상 호 수·기준값 합계·편집 가능 여부.
removeChargeLines(chargeCode)항목 삭제 시 allocation line과 지침 동반 정리.

4. 부과항목 데이터 구조

useCharges.charges 각 항목이 가지는 필드:

필드분류설명
chargeCode등록코드(코드체계 §1-3)생성 시 자동 부여(ci-N 시퀀스), 이후 불변. FK 안정 키.
name이름(명)표시명. 수정 가능.
nature정의BILLING_NATURE 키 중 하나(관리비/수도료/장기수선충당금/예비비적립금). 분개 라우팅 핵심 — enum 외 값 금지.
taxCategory정의과세/면세/해당없음.
taxInvoiceType정의일반/계산서/없음.
chargeBasis정의전용/계약/사용량/고정지분/가변지분. chargeBasis.js에서 라벨·방식·계산식을 파생.
active메타true/false. false이면 activeCharges 제외 → 부과 사슬 전체 제외.
baseChargePrincipal 외 금액 필드인스턴스부과(charging) 화면에서 v-model 편집. 설정>항목에서 표시·편집 안 함.

single-canon: 정의 필드와 인스턴스(금액) 필드가 같은 charge 객체 안에 공존. 설정>항목 = 정의 편집 담당, charging/assessment = 금액 편집 담당.


5. 리스트 IA — DynamicTableOrg.vue

컬럼(순서): 체크박스(다중선택) · 활성(토글) · 항목명(주식별자) · 성격 · 세금구분 · 세금계산서유형 · 배분기준.

  • 항목명 클릭 = 상세 시트(sheet-read-charge-item-art) 진입. selectItem(chargeCode) + command="show-modal". 클릭 affordance: cursor-pointer font-medium hover:underline.
  • 활성 체크박스 = toggleActive(chargeCode, on) 즉시 호출 → activeCharges 반영.
  • 체크박스 = toggleCheck → DataToolBar 삭제 대상.
  • 금액 컬럼 없음 — charging 소관임을 상기.

6. 오버레이 — 추가 시트 SheetCuItemArt.vue

id="sheet-create-charge-item-art". 유형 B 단일 폼 시트(컬렉션 없음, CLAUDE.md §1-B).

  • 필드: 항목명(text input) · 성격(Select, BILLING_NATURE 키만) · 세금구분(Select) · 세금계산서유형(Select) · ChargeBasisFieldOrg.
  • 폼은 배분 방식을 먼저 고른 뒤 필요한 세부 필드만 보인다: 면적 비례→전용/계약, 지분 비례→고정/가변, 사용량 비례→추가 선택 없음.
  • 선택 아래 배분 계산 카드가 chargeBasisFormula·chargeBasisDescription을 표시한다. 설명문을 폼 곳곳에 반복하지 않고 현재 선택의 계산 계약만 상시 노출한다.
  • 모바일은 grid-cols-1, 데스크톱은 md:grid-cols-2; Select trigger 폭은 항상 w-full.
  • 성격 select는 BILLING_NATURE 키만 허용 — 자유입력 없음. 목적: 분개(journal-by-nature) 라우팅이 BILLING_NATURE를 키로 쓰므로 미정의 값이 들어오면 분개 fallback 발생.
  • 주석: "금액은 부과(청구 산정) 화면에서 입력합니다." 안내.
  • submit(): 항목명 필수 검증 후 addCharge()ensureChargeLines(chargeCode) → form reset → 시트 close. 지분·사용량 초깃값은 0이며 면적은 unit master를 읽는다.

7. 오버레이 — 상세 시트 SheetReadItemArt.vue

id="sheet-read-charge-item-art". 항목 속성과 연결 컬렉션(세대별 배분 값)을 함께 갖는 허브 시트. 폭 sheet-width-2xl.

  • selectedCharge = useCharges().charges.find(c => c.chargeCode === selectedCode) computed.
  • zone-top: 항목명(title-sm) + 세금구분 badge + chargeCode(subtitle-sm, 우측).
  • READ 모드: disclosure + table-static(항목 정의 5행). 하단 "금액은 부과 화면에서 관리합니다." 안내.
  • EDIT 모드: 단일 card 안에서 항목 정의와 배분 설정을 평면 섹션으로 나눈다. ChargeBasisFieldOrg를 생성 시트와 공유해 선택지·조건·계산식 drift를 막는다. CLAUDE.md §0-6 EDIT 모드 = 전부 펼침, 접기 없음.
  • READ 모드: 배분방식 라벨(chargeBasisLabel)과 동일한 계산식 요약을 표시한다.
  • 연결 컬렉션: 세대별 배분 값 disclosure가 대상 호 수와 합계를 보여준다. 면적이면 [값 보기], 사용량·지분이면 [값 관리]SheetManageChargeBasisValuesArt를 연다. 항목 정의 edit 모드에서는 속성 커밋에 집중하도록 이 컬렉션을 표시하지 않는다.
  • sheet-footer 토글: READ = [편집](button-neutral-subtle), EDIT = [취소] + [저장](button-primary-bold).
  • saveEdit(): updateChargeDef(selectedCode, { ...draft })isEditing = false. 금액·chargeCode는 DEF_FIELDS 외라 패치 불가.
  • 저장 가드(FB-18, 2026-07-10): editValid = computed(() => !!draft.value.name?.trim()). saveEdit()!editValid.value면 조기 return, [저장] 버튼도 :disabled="!editValid". 공백 이름 저장을 막는다 — 항목명이 목록 주식별자이자 검침 CSV(importCsv)의 이름 해석 키라 비면 그 항목을 다시 찾을 수 없게 되는 문제(감사 FB-18)를 차단. SheetCuItemArt.vue(생성 시트)의 기존 필수 검증과 대칭. 커밋 5f030f138.

7-1. 호별 배분값 관리 시트

SheetManageChargeBasisValuesArt.vue, id="sheet-manage-charge-basis-values-art", 집중 폭 sheet-width-3xl.

  • 헤더: 항목명·부과기준·관리코드.
  • 계산 카드: chargeBasisFormulachargeBasisDescription을 항목 상세와 동일하게 사용.
  • 값 표: 호/기준값 2열, max-h-96 overflow-y-auto + sticky-thead; 시트 안 페이지네이션 없음.
  • 면적: unit.exclusiveArea/unit.contractArea를 읽기 전용 표시하고 프로퍼티 설정이 수정 원천임을 안내.
  • 사용량·지분: input input-bordered input-sm, inputmode="decimal", 0 이상 숫자 입력. 저장은 dirty && valuesValid일 때만 활성.
  • 모바일: 열이 두 개뿐인 고정 정보 구조, 값 입력 폭 w-32; 시트 자체가 모바일 뷰포트에서 전폭으로 동작하고 내부 표만 세로 스크롤한다.
  • 취소/close는 resetDraft, 저장은 commitBasisValues 후 close.

7-2. 부과산정 표에서 상세 재사용

src/components/service-charge/actual/console/charging/assessment/에서도 항목 상세 두 컴포넌트를 그대로 마운트한다.

MainOrg.vue
├── DynamicTableOrg.vue
├── SheetReadItemArt.vue
└── SheetManageChargeBasisValuesArt.vue
  • DynamicTableOrg.vue의 항목명 버튼: selectItem(charge.chargeCode) + command="show-modal" commandfor="sheet-read-charge-item-art".
  • 클릭 affordance는 설정 목록과 동일한 font-medium text-left cursor-pointer hover:underline.
  • 과거 AccountDetailArt.vue는 선택 항목과 연결되지 않은 정적 mock이라 이 진입점에서 마운트하지 않는다.
  • 오른쪽 액션 열은 account-charge-detail(차수의 부과 금액 상세)만 유지한다. 항목명과 같은 상세를 여는 중복 아이콘 버튼은 두지 않는다.
  • 결과적으로 항목명 = 항목 정의·배분값 상세, 계산기 아이콘 = 당기 부과 금액 상세로 역할이 분리된다.

7-3. 벌크 삭제 확인 다이얼로그 (FB-24/30, 2026-07-10)

DataToolBarOrg.vue[삭제] 버튼은 더 이상 checkedCodes를 즉시 removeCharge로 넘기지 않는다. 대신 command="show-modal" commandfor="dialog-delete-charges-art"DialogDeleteChargesArt.vue(신규, overlays/)를 연다 — 원클릭 무확인 삭제(FB-24)를 확인 다이얼로그 경유로 전환.

  • DialogDeleteChargesArt.vue: useChargeItemUi().checkedCodes 대상 건수 안내 → [삭제] 클릭 시 항목별 removeChargeLines(code) + removeCharge(code) + clearChecked(). 배분 라인·검침 지침 고아 데이터를 남기지 않는다.
  • 마운트: MainOrg.vue(SheetCuItemArt/SheetReadItemArt를 렌더하는 동일 부모)에 <DialogDeleteChargesArt /> 추가.
  • DataToolBarOrg.vue는 이제 checkedCodes.length === 0 기반 disabled 계산만 소비 — removeCharge import·직접 호출 없음.
  • 같은 커밋(5f030f138)이 선수금 환급(AdvanceBalanceOrg.vue, billing/_core/console/collecting/detail/blocks/)에도 동형 확인 다이얼로그(dialog-confirm-refund-advance-art)를 적용(FB-30) — 두 삭제/환급류 액션을 같은 정책(확인 다이얼로그 경유)으로 통일.

7-4. 페이지네이션 "N개 선택됨" 배지 (FB-27, 2026-07-10)

PaginationToolBarOrg.vue(설정>항목)가 useChargeItemUi().checkedCodes를 import해 배지를 v-if="checkedCodes.length" + {{ checkedCodes.length }}개 선택됨으로 실바인딩(이전엔 항상 "10개 선택됨" 하드코딩). 초기 데이터(setting/initial-data/general) 쪽의 동형 배지는 선택 모델 자체가 없음(체크박스가 v-model 미연결 장식용)을 확인 후 배지+wrapper를 통째로 제거했다 — 없는 기능을 있는 것처럼 보이는 허위 표시 제거가 목적이라, 실 선택 모델이 없는 화면은 "0개"로 얼버무리지 않고 배지 자체를 없앴다.


8. activeCharges 부과 사슬 전파

toggleActive(chargeCode, false) 또는 removeCharge(chargeCode)activeCharges에서 즉시 제외. 다음 소비처가 reactively 재계산:

소비처파일라인
배분(할당) 산정src/composables/useAllocations.jsL21 activeCharges as allCharges
청구 집계 상세src/composables/useInvoicingDetail.jsL17 activeCharges as charges (L160 루프 자동 반영)
부과 산정 테이블src/components/service-charge/actual/console/charging/assessment/blocks/DynamicTableOrg.vueactiveCharges as charges
배분 피벗 — 유닛src/components/service-charge/actual/console/charging/allocation-unit/blocks/DynamicTableOrg.vueactiveCharges as charges
배분 피벗 — 계약src/components/service-charge/actual/console/charging/allocation-contract/blocks/DynamicTableOrg.vueactiveCharges as charges
배분 피벗 — 멤버src/components/service-charge/actual/console/charging/allocation-member/blocks/DynamicTableOrg.vueactiveCharges as charges

전파 방향: 설정>항목 활성 토글 → activeCharges → 배분 산정 → 배분 피벗 3화면(유닛/계약/멤버) → 청구 집계 → 수납 → 분개 → 고지서.

시드는 전부 active: true이므로 활성 전환 이전 동작은 회귀 없음(기존 vitest 전부 green).


9. chargeCode 코드체계 (CLAUDE.md §1-3 등록코드)

  • 생성: addCharge()nextChargeCode()ci-{seq} 형태(기존과 충돌 방지 while loop).
  • 수정: 불가(DEF_FIELDS에 없음, updateChargeDef 패치 대상 아님).
  • 삭제: removeCharge()로 영구 제거. 삭제 후 시퀀스 재사용 안전(충돌 방지 loop).
  • 고객 노출: 상세 시트 zone-top 우측 subtitle-sm으로 최소 노출.
  • 목록 비노출(정책 준수).

10. nature enum 제약

BILLING_NATURE 키 = src/composables/billingNature.js export. 현재 값: 관리비 · 수도료 · 장기수선충당금 · 예비비적립금.

신규 항목 추가 시 반드시 이 중 하나 선택. 선택 UI(SheetCuItemArt, SheetReadItemArt EDIT)는 Object.keys(BILLING_NATURE)를 Select 옵션으로 렌더링 — 임의 문자열 입력 불가.

신규 nature 종류를 추가하려면 billingNature.js에 먼저 등록 + useBillingJournal(분개 라우팅) 확장이 선행돼야 한다(범위 외).


11. 확장 포인트

  1. 다기간 정의/인스턴스 분리(미래): 현재 charge 객체가 정의(name/nature 등)와 인스턴스(금액)를 겸한다. 기간별로 다른 금액이 필요해지면 charges = 정의 배열, chargeInstances = 기간×항목 금액 배열로 분리. demoSeed.buildSeed() 팩토리가 그 패턴을 미리 준비(defs 배열에서 instantiate)하므로 이 분리는 시드 팩토리 교체로 시작.
  2. 실 API 백킹: 현재 모듈 싱글톤(데모). 실제 API 연동 시 charges ref를 스토어/API 반응형으로 교체. addCharge/removeCharge/updateChargeDef/toggleActive 계약은 유지.
  3. 항목 그룹: setting/item/group은 데이터모델(그룹 스토어) 자체가 없어 여전히 별도 트랙이지만, 2026-07-10(펀치리스트 I-1)에 허위 표시는 제거했다 — DynamicTableOrg.vue의 인라인 mock 행(accounts = ref([...]))을 ref([]) + 빈상태 안내 행("그룹 관리가 열리면…")으로 교체하고, DataToolBarOrg.vue의 생성/삭제/다운로드 버튼을 disabled title="준비 중이에요"로 정직 비활성화했다(974788f2c). 실제 그룹 CRUD 구현은 여전히 미착수.
  4. leysys-design SSOT: card/field/sheet/table/badge 클래스는 전부 SSOT 카피본. DS 결함 즉석 패치 금지.
  5. 배분기준 확장: 신규 기준은 chargeBasis.js enum·normalize·method·label·formula·description을 먼저 추가하고 useAllocations.basisQuantity를 연결한 뒤 UI에 노출한다. UI 단독 옵션 추가 금지.

12. 배분방식 정본 (2026-07-13)

src/composables/chargeBasis.js가 저장 값 호환과 표시 계약을 소유한다.

캐논 값방식조건부 세부선택계산 데이터
전용 / AREA_EXCLUSIVE면적 비례전용면적line.basis.area
계약 / AREA_CONTRACT면적 비례계약면적line.basis.contractAreaunit.contractArea → 구데이터 area fallback
사용량 / USAGE사용량 비례없음useMeterReadings().usageOf()
고정지분 / FIXED_SHARE지분 비례고정지분line.basis.fixedShare
가변지분 / VARIABLE_SHARE지분 비례가변지분line.basis.variableShare

분양면적은 저장 값·UI 선택지·공식에 존재하지 않는다. 면적 방식은 전용/계약 두 값만 허용한다.