Skip to content

관리비 고지서(invoice) — FE 메인테이너 매뉴얼

독자: FE 메인테이너/AI. 관리비 고지서(3단 절취 A4)의 컴포넌트 트리·데이터 흐름·확장 포인트. 정본 spec: docs/superpowers/specs/2026-06-27-invoice-wiring-design.md · 항목별 영수증 2026-06-27-invoice-itemized-receipt.md. 2026-06-27 실데이터 배선.

1. 라우트·진입

  • 라우트: /document/transaction/invoice-dynamic-view(동적·실데이터) · /invoice-static-view(정적 데모). router/index.js.
  • 진입: ① 사이드바 "관리비고지서"(components/document/AppAsideOrg.vue) ② 청구·수납 상세 시트(SheetReadBillingArt)의 sheet-footer [고지서] 버튼selectedBilling(시트 진입 시 세팅) 유지된 채 openInvoice(동일 라우트). 청구탭(유닛이름)·수납탭에서 이 시트가 열림.
  • 진입 일원화(2026-06-28): 과거 총청구금 클릭 시트(SheetInvoicingDetailArt)의 [고지서 보기]는 제거. 같은 청구 테이블에서 두 상세 시트로 갈리고 고지서 접근이 한쪽뿐이던 불일치 해소 → 고지서 진입은 SheetReadBillingArt로 통일.
  • 콘솔 "고지서 미리보기" 모달(2026-06-29): service-charge 3개 콘솔 페이지(console/general·notice/general·notice/individual)의 툴바 "고지서 미리보기" 버튼이 <dialog id="invoice-preview-art" class="sheet sheet-width-5xl">InvoiceDynamicOrg를 임베드(<div class="w-full h-full flex flex-col overflow-auto">)한다. 구 mock InvoicePreviewArt 3사본 삭제. provisional/console/general은 잠정정산(이월 부재)이라 제외·mock 유지. 진입 경로 요약: ①청구 상세 [고지서]→/document/transaction/invoice-dynamic-view ②콘솔 "고지서 미리보기" 모달.
  • 데모 폴백: selectedBilling 비었으면 첫 과세 계약 자동 선택(InvoiceDynamicOrg).

2. 컴포넌트 트리 (3단 절취 구조)

InvoiceDynamicView → InvoiceDynamicOrg (.print.print-a4-portrait.print-pmf)
  vm = computed(() => useInvoice().invoiceFor(selection))   ← 데이터 단일소스
  ├ 1단 section-100: HeaderAto · OverviewAto(:payable·납기) · RecipientAto(:세대)
  ├ ✂ 절취선(divider-y dashed)
  ├ 2단 section-200: ContentAto·NoticeAto·DiscountTableAto(:discounts) / UtilityUsageAto(:rows=metering.usage)·ComparisonAto(:rows=metering.comparison)·PaymentInfoAto
  ├ ✂ 절취선
  └ 3단 section-300: InvoiceTableMinDynamicAto(의뢰서 :byTaxGroup) · InvoiceTableMaxDynamicAto(영수증 :lineItems 26·:summary)
  • 블록 = presenter(props 받아 표시만). 미주입 시 데모 폴백 default. script setup + defineProps.
  • 배선된 블록(실데이터): Overview·Recipient·Min·Max·Discount·UtilityUsage·Comparison(검침/수도). 미배선(데모 상수 승계): Header·Content·Notice·PaymentInfo.

3. 데이터 흐름

useInvoice().invoiceFor({axis,key}, asOf=REFERENCE_DATE) → InvoiceVM
  useInvoicingDetail.detailFor (당기 supplyTotal·vat·forward) +
  useAllocations.invoicingBy*().currentPeriodByNature (성격별 집계, ★신설) +
  useCollectingDetail.detailFor (payable·선수금·연체료) +
  useMetering.meteringFor (검침 수도 — 지침/사용량/공급가, ★신설) +
  invoiceDisplayPreset.expandComposition(byNature,'관리비',GENERAL_FEE_COMPOSITION) (과세 관리비 lump → 비목 분해)
    → buildDisplayLineItems(expanded) (성격→표준 26항목 N:1)

InvoiceVM: header{invoiceCode,periodLabel,dueDate,manager,recipient} · summary{세전부과원금,부가세,미납원금,미납연체금,합계,선수금,청구액,당월수납액,당월발생연체,이월발생연체} · byTaxGroup{과세합계,면세합계,영세합계,계산서비대상,비매출합계,부가세}(의뢰서) · lineItems[26]{key,label,taxClass,amount}(영수증) · metering{usage[],comparison[],unitPriceMode}(검침 — 수도 없으면 null) · forward · payment · unmapped.

  • summary.당월수납액·당월발생연체·이월발생연체(FB-16, 2026-07-10): useCollectingDetail.detailFor가 반환하는 col.periods[](각 {kind:'current'|…, collected, lateFee:{total}})에서 kind==='current'인 행만 필터해 파생. 당월수납액 = Σcollected · 당월발생연체 = ΣlateFee.total · 이월발생연체 = 미납연체금 − 당월발생연체(전체 미납연체금에서 당월분을 뺀 나머지 — 불변식: 이월발생연체+당월발생연체===미납연체금). 이전엔 Min/Max 템플릿이 이 세 값을 fmt(0)으로 하드코딩해 항상 0으로 보였다(청구액 자체는 정확했음). 합계 공식(세전부과원금+부가세+미납원금+미납연체금)은 불변 — 전부 표시 분해일 뿐 금액 재계산 아님.

  • byTaxGroup.비매출합계(FB-17, 2026-07-10): sumByTaxClass(rows).비매출(장기수선충당금 등 매출 아닌 부과)을 노출. Min(의뢰서) 템플릿에 영세율합계·장기수선충당금 구분행을 추가해, "구분행 열거합 = summary.합계" 불변식이 항상 성립하도록 정합(이전엔 Min만 이 두 항목이 빠져 열거합이 합계보다 작게 보였다 — Max는 원래 정합).

  • 청구액 = useCollectingDetail.payable 직접사용(재계산 금지). 7행 요약은 표시 분해(billing time 정합).

  • lineItems = byNature → 표준 26항목. 수도료→수도료·장충→장기수선비 1:1, 나머지 0. 세금 마커 *면세 **계산서비.

  • 항목별 영수증(구성내역 분해): 과세 관리비 통합 lump를 expandCompositionGENERAL_FEE_COMPOSITION(과세 비목 구성비 Σ=1)으로 영수증 표시 단계에서만 pro-rata 분해(인건비·경비비·청소비… 9비목). 부과·청구·수납·이월은 통합 1줄 유지(이 분해 미경유 — demoSeed·useAllocations 무변경). 최대잔여법(Hamilton)으로 Σ보존(정수원·Σ=1 전제). 9 sub-nature 전부 과세 → 과세 총액 불변(byTaxGroup.과세합계 정합).

  • 검침(수도 사용량) — useMetering: 수도료(a1b1c2)를 chargeBasis '사용량' 부과화. meteringFor(selection)usage(호별 지침/사용량, demoSeed.meterReadings) + comparison(당월공급가=캐논 byNature 수도료=영수증 reconcile / 전월공급가=전월풀×가중share / 산정·지정단가). 당월공급가는 useAllocations 캐논 재사용(독립 재계산 아님). 빌링코어 무편집(풀 보존 I1=잔차흡수 구조보장). 표시 단가 = 산정 고정(지정단가는 모델값, 토글은 어셈블 화면). 수도 1행(전기/온수/가스 미모델 → 블록 빈행 비노출). ⚠ usage 가중(usage×일수비율)useMetering이 독립 계산 — 빌링코어 useAllocations.lineWeight와 동일 공식이나 export 부재로 중복(후속: 가중공식 export로 SSOT화 권장).

4. 확장 포인트

  • 추가 디자인: 블록이 presenter라 교체·변종 용이. 간이/상세 = 블록 집합 다르게 조합(InvoiceStaticOrg 패턴). 타 빌링 = useInvoice 라인 어댑터 분리. 납부서/영수증 분리 = 3단 절취 독립 출력.
  • 표시 태그 커스터마이즈: invoiceDisplayPreset.APARTMENT_NATURE_MAPPING 단지별 override. 빌딩유형 프리셋(집합건물) 추가 가능.
  • 비목 구성비 커스터마이즈: GENERAL_FEE_COMPOSITION(Σ=1) 단지별 override → 영수증 비목 펼침이 자동 반영. 빌딩유형별 구성비 프리셋 추가 가능. ⚠ override 시 Σ=1 유지(아니면 영수증 과세합 silent drift — 테스트가 정본 프리셋 Σ=1 잠금).
  • 항목별 영수증 ✅완료: 구성내역 분해(expandComposition). 실 BE 연동 시 구성비를 charge/building config로 이관하면 동일 분해 자동 작동(itemize 로직 재사용).
  • 검침 멀티유틸 확장: useMetering은 수도 1행 반환 구조이되 멀티 유틸 확장 가능(전기/난방/온수 charge를 chargeBasis '사용량'+meterReadings로 추가 → usage/comparison 행 자동 증가). 빌딩유형 프리셋이 건물별 검침 유틸 집합 선택. 다유닛 selection(member) 시 usage 호별 다행(현재 라벨은 utility만 — 호 구분 표기 후속).

5. 테스트 위치

  • __tests__/invoiceDisplayPreset.spec.js — 26항목·매핑·N:1·세금구분(독립출처) · GENERAL_FEE_COMPOSITION(Σ=1·전부 과세 강제)·expandComposition(Σ보존·잔차 흡수·비파괴·과세 총액 불변).
  • __tests__/useInvoice.spec.js — 청구액=payable·합계정합·byNature 교차검증·Σ영수증=부과원금·null-safe·3축 · 비목 분해 표시(laborCost<과세원금·경비/청소>0·Σ과세행=과세원금·byTaxGroup 정합) · (2026-07-10) FB-17: 의뢰서 구분행 열거합(과세+면세+영세+비매출+계산서비+VAT+미납원금+미납연체금)=summary.합계 · FB-16: 이월발생연체+당월발생연체=미납연체금, 당월수납액≥0.
  • __tests__/useMetering.spec.js — I1 풀보존·I3 지침일관·I4 reconcile(당월공급가=byNature=영수증 line)·I5 전월·산정/지정단가·범위경계(수도만·prior-only null).
  • blocks/__tests__/MeteringBlocks.spec.js — UtilityUsage·Comparison 실값 렌더(지침165/130·사용35·공급가)·999,999/888,888 폐기·빈상태 안내.
  • 브라우저 검증: headless probe(비목 9분해 실값·콘솔 0). 검침은 컴포넌트 mount 렌더 테스트로 대체(브라우저 미연결 환경).

6. 알려진 한계

  • 비목 구성비는 표시계층 표준값(데모) — 실 분개 기준 비목 금액 아님(실 BE가 charge/building config로 이관 시 정밀화). taxClass 비데모 23항목 잠정(검증대기).
  • 검침: 수도 1개만 부과화(전기/난방/온수/가스 미모델 → 블록 빈행 비노출). 전월공급가는 표시-추정(전월풀=당월풀×factor, per-selection 반올림 합이 전월풀과 ±1원 — 영수증 reconcile 대상 아님). 다유닛 selection usage 라벨 호 미구분. 가중공식(usage×일수비율) useMetering 독립 재계산useAllocations.lineWeight와 동일하나 export 부재로 중복(가중공식 export로 SSOT화 권장).
  • Static 변종(InvoiceStaticOrg)·NoticeAto·PaymentInfoAto는 미배선 데모 상수. InvoicePreviewArt 3사본(console/general·notice/general·notice/individual) 삭제·정본 InvoiceDynamicOrg 임베드로 교체 완료(2026-06-29). provisional/console/general은 잠정정산(이월 부재)이라 mock 유지.