Skip to content

수납현황 — FE 메인테이너 참고

관리비 상용화 필수 문서 트랙(펀치리스트 점검 ④)의 3호. 설계 정본: docs/superpowers/specs/2026-07-10-charge-statement-collection-status-design.md. 데이터 계약은 docs/handoff/backend/report-collection-status.md 참고. ①총괄표 FE handoff(report-summary-table.md) 패턴 복제.


1. 라우트 / feature 게이트

1-1. 콘솔 탭

신규 탭 없음 — 부과내역서와 동일하게 기존 보고서 탭(feature:'reports')의 내부 세그먼트로 편입(§3 ReportKindTabsAto).

1-2. 페이지 라우트 (buildLineRoutes.js)

editions.actual.ownreport/collection-status 키:

js
'report/collection-status': () => import('../views/service-charge/actual/console/report/collection-status/IndexView.vue')

provisional에는 없음 — 라우트 부재로 게이트(테스트: buildLineRoutes.spec.js).

1-3. 인쇄 문서 라우트

/document/report/collection-status-view → views/document/report/CollectionStatusView.vue

feature 게이트 없음(document 앱 공통 패턴).

1-4. i18n

신규 키 없음(§ 부과내역서 handoff 1-4와 동일 근거).


2. 데이터 컴포저블 — useCollectionStatus

위치: src/composables/useCollectionStatus.js.

API 시그니처:

  • statusRows()useReceivableAging().agingByAxis('unit') 그대로 반환(가공 없음). 각 행은 key·name·전기이월·당기발생·회수·조정·차기이월·연체차수·anomalous·periods.
  • statusTotals(rows = statusRows()) → 5개 열(전기이월·당기발생·회수·조정·차기이월)의 합산 객체.
  • formatAmount → 재-export.

2-1. 호출 관용 — rows를 한 번 계산해 재사용

총괄표 columnTotals/부과내역서와 동일 관용: 컴포넌트에서 rowscomputed로 한 번 감싸고 statusTotals(rows.value)처럼 명시 전달한다. 인자 없이 호출하면 내부에서 statusRows()(=agingByAxis('unit'))를 또 호출해 중복 스캔이 발생한다. MainOrg.vue가 이 관용을 그대로 따른다(const totals = computed(() => statusTotals(rows.value))).


3. 보고서 종 세그먼트

부과내역서 handoff §3과 동일 — ReportKindTabsAto.vue를 그대로 재사용한다. 이 문서에서 반복하지 않음.


4. 컴포넌트 구조

  • src/views/service-charge/actual/console/report/collection-status/IndexView.vue — 콘솔 페이지 shell(공용 report/_shared/HeaderOrg[기간 헤더+콘솔 탭 레일] + MainOrg — 2026-07-10 후속: 헤더 누락 정정).
  • src/components/service-charge/actual/console/report/collection-status/MainOrg.vue — 웹조회: ReportKindTabsAto + roll-forward 표(table table-sm table-divide-y sticky-thead) + [인쇄] 버튼 1개(총괄표·부과내역서와 달리 뷰 토글·복수 인쇄 버튼 없음 — 단일 서식).
  • src/views/document/report/CollectionStatusView.vue — 인쇄 문서 뷰.

4-1. anomalous 배지 렌더

html
<td class="font-medium">
  {{ r.name }}
  <span v-if="r.anomalous" class="badge badge-sm badge-red-subtle">확인필요</span>
</td>

웹조회 MainOrg에만 배지가 있다(CollectionStatusView.vue 인쇄 뷰는 배지 미포함 — 인쇄물은 흑백 프린터 대응 위해 텍스트만, 컬러 배지 강조는 화면 전용). 인쇄 뷰에 배지가 필요해지면 흑백 대비 표시(예: [확인필요] 텍스트 접두)로 별도 검토 필요.


5. 인쇄 뷰 패턴

CollectionStatusView.vue는 총괄표·부과내역서와 동일한 print-a4-portrait-flow-scoped(폭 190mm, 높이 auto)를 재사용한다. 열이 7개(세대·전기이월·당기발생·회수·조정·차기이월·연체차수)로 세로 용지에 여유가 있어 landscape 전환이 필요 없다 — 총괄표의 named @page(landscape) 패턴이 이 문서에는 적용되지 않는다.

단지 1장(세대 수만큼 세로로 길어짐) 문서라 대량 인쇄 파이프(njk) 대상이 아니다 — 부과내역서·고지서 묶음과 달리 세대별 개별 블록/페이지가 아닌 단일 표이기 때문(문서류 매트릭스 §6 참고).


6. [시트] — xlsxExport 유틸 계약 (2026-07-11 후속, B-3)

js
import { periodOf, periodLabelOf } from "@/composables/useBillingPeriod";
import { exportXlsx } from "@/lib/report/xlsxExport";

const XLSX_COLUMNS = [
  { key: "name", label: "세대" },
  { key: "전기이월", label: "전기이월" },
  { key: "당기발생", label: "당기발생" },
  { key: "회수", label: "회수" },
  { key: "조정", label: "조정" },
  { key: "차기이월", label: "차기이월" },
  { key: "연체차수", label: "연체차수" },
];
const downloadXlsx = () =>
  exportXlsx({
    fileName: `수납현황-${periodLabelOf("service-charge", "actual")}.xlsx`,
    sheetName: "수납현황",
    columns: XLSX_COLUMNS,
    rows: rows.value,
  });
  • exportXlsx({ fileName, sheetName, columns, rows })(src/lib/report/xlsxExport.js, SheetJS write-only) — rows.value(=statusRows(), prior-only 세대 포함 전체)를 그대로 export한다. 화면의 [확인필요] 배지(anomalous)는 시트에 별도 열로 내보내지 않는다 — 연체차수 숫자로 동일 정보를 유추할 수 있다(§4-2 배지 기준 overdueCount >= 2).
  • 값 규율: 숫자는 raw number로 기록 — rows.value를 그대로 넘기므로 자동으로 지켜진다.
  • 배선 스모크: src/components/service-charge/actual/console/report/__tests__/XlsxWiring.spec.js — 5보고서 공통.

7. 알려진 미해결 사항

  • document 앱 셸 no-print 갭 — 총괄표 §6와 동일 근거로 스코프 밖.
  • 수납 채널별 분해 없음(§ backend handoff §4-3) — 대사 3채널 트랙(파킹 중)과 연계될 후속 사항.

8. 테스트 위치

  • src/composables/__tests__/useCollectionStatus.spec.js — 불변식 4종(backend handoff §3 대응). 특히 불변식④가 시드 202호(prior-only)의 존재를 가드 — 이 테스트가 깨지면 useReceivableAging/useForwarding 쪽의 행집합 변경이 원인일 가능성이 높다.
  • src/router/__tests__/buildLineRoutes.spec.js — actual 전용 게이트.
  • src/components/service-charge/actual/console/report/__tests__/XlsxWiring.spec.js[시트] 배선 스모크(수납현황 케이스 포함).
  • Playwright 라이브 실측: 수납현황에 202호(prior-only·당기발생 0) 행 존재 확인, [시트] 다운로드, 인쇄 뷰 개봉 — SDD plan §검증 절 참고.

9. 확장 절차

docs/handoff/frontend/report-summary-table.md §8의 일반 절차를 따름. 채널별 회수 분해가 실제로 추가될 때는 이 문서(§7)와 backend handoff(§4-3·§5)를 함께 갱신할 것.

2026-07-10 후속 2 (SSOT 정합): 웹조회 MainOrg는 leysys-design 정본 pattern-app/transaction/table/static-table 패턴을 따른다 — container 루트 > 보고서 종 세그먼트(page-header-below p-1 bg-neutral-minimal rounded-full 필 스트립) > card card-md(card-header bg-neutral-minimal inset-edge-b 툴바 + card-body bg-neutral-minimal 표). 테이블은 정본 static 계열 table table-hover table-sm table-static table-divide-y(장표는 +sticky-thead). 미수금 연령분석 축탭도 동일 필 스트립·h-fit 래퍼(flex shrink 압축 방지).