Skip to content

산출표 — FE 메인테이너 참고

관리비 상용화 보고서 확충 트랙(2026-07-11, B-1). 설계 정본: docs/superpowers/specs/2026-07-11-period-tag-minors-reports-xlsx-design.md §B-1. 데이터 계약은 docs/handoff/backend/report-calculation-table.md 참고. 총괄표 FE handoff(report-summary-table.md) §1~§8 패턴을 그대로 복제 — 본 문서는 산출표 고유 지점만 서술.


1. 라우트 / feature 게이트

1-1. 콘솔 탭

신규 탭 없음 — 기존 보고서 탭(feature:'reports')의 내부 세그먼트(ReportKindTabsAto, §3)로 편입. KINDS 배열의 맨 앞에 추가돼 산출표가 보고서 세그먼트 중 가장 왼쪽에 위치한다.

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

editions.actual.ownreport/calculation-table 키:

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

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

1-3. 인쇄 문서 라우트

/document/report/calculation-table-view → views/document/report/CalculationTableView.vue

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

1-4. i18n

신규 키 없음(ReportKindTabsAto의 세그먼트 라벨은 컴포넌트 로컬 리터럴).


2. 데이터 컴포저블 — useCalculationTable

위치: src/composables/useCalculationTable.js. 순수 함수 컴포저블(라우터/컴포넌트 의존 없음).

API 시그니처:

  • calculationRows() → 활성 항목 배열. 각 원소는 chargeCode·name·chargeBasis·수량·단가·발생금액·할인금액·부과금액.
  • calculationTotals(rows = calculationRows())발생금액·할인금액·부과금액 합산 객체. rows 인자를 생략하면 내부에서 calculationRows()를 재호출(총괄표 columnTotals 관용과 동일 — 명시 전달 권장).
  • formatAmountuseCharges에서 재-export.

3. 보고서 종 세그먼트 — ReportKindTabsAto

부과내역서 handoff §3과 동일 컴포넌트(src/components/service-charge/actual/console/report/_shared/ReportKindTabsAto.vue) — KINDS 배열에 { key: 'calculation-table', label: '산출표', path: '...report/calculation-table' } 한 줄이 맨 앞에 추가됐다(신규 보고서 종 추가 시 배열 순서 = 화면 노출 순서). 현재 5종: 산출표 → 총괄표 → 부과내역서 → 부과요약서 → 수납현황.


4. 컴포넌트 구조

  • src/views/service-charge/actual/console/report/calculation-table/IndexView.vue — 콘솔 페이지 shell(공용 report/_shared/HeaderOrg + MainOrg).
  • src/components/service-charge/actual/console/report/calculation-table/MainOrg.vue — 웹조회: ReportKindTabsAto + 항목별 표(table table-hover table-sm table-static table-divide-y sticky-thead) + [시트][인쇄] 버튼.
  • src/views/document/report/CalculationTableView.vue — 인쇄 문서 뷰.

MainOrg는 read-only 조망. 화면·인쇄 양쪽 모두 useCalculationTable을 직접 호출한다(별도 인쇄 전용 VM 없음 — 숫자 정본 한 곳).


5. 인쇄 뷰 패턴

CalculationTableView.vue는 총괄표·부과내역서·수납현황과 동일한 print-a4-portrait-flow-scoped(폭 190mm = 210 − @page margin 10mm×2, 높이 auto)를 재사용한다. 열이 7개로 세로 용지에 여유가 있어 landscape 전환이 필요 없다. 신규 CSS 없음(기존 flow-scoped 클래스 그대로 컴포넌트 scoped 스타일에 재선언).


6. [시트] — xlsxExport 유틸 계약

js
import { exportXlsx } from "@/lib/report/xlsxExport";
const XLSX_COLUMNS = [
  { key: "name", label: "항목" },
  { key: "chargeBasis", 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(xlsx 패키지) write-only 사용(파일 파싱/read 경로 없음 — read-path 취약점 비해당). columns{ key, label } 배열, rows는 화면 rows 그대로(별도 산식 없음 — 화면=시트 동치).
  • 값 규율: 숫자는 raw number로 기록한다(화면 포맷 문자열 fmt() 결과를 넣지 않음) — 엑셀에서 바로 합산 가능해야 하기 때문. rows.value를 그대로 넘기므로 이 규율은 컴포저블 반환값이 이미 숫자 타입이기만 하면 자동으로 지켜진다(별도 변환 불요).
  • 파일명 규약: <보고서명>-<periodLabel>.xlsx(세대 선택이 있는 부과내역서만 -<unitName> 추가, report-charge-statement.md §xlsx 참고).

7. 알려진 미해결 사항

  • document 앱 셸 no-print 갭 — 총괄표 §6와 동일 근거로 스코프 밖.
  • 기간범위(월 구간)·층/블록 축 필터 없음 — backend handoff §5-6 참고.

8. 테스트 위치

  • src/composables/__tests__/useCalculationTable.spec.js — 불변식 3종(backend handoff §3 대응).
  • src/router/__tests__/buildLineRoutes.spec.js — actual 전용 라우트 게이트.
  • src/components/service-charge/actual/console/report/__tests__/XlsxWiring.spec.js — 5보고서 [시트] 배선 스모크(산출표 케이스 포함) — 버튼 클릭 시 exportXlsx가 화면 rows로 1회 호출되는지 확인.
  • Playwright 라이브 실측: 세그먼트 진입 순서(맨 앞), [시트] 다운로드, 인쇄 뷰 개봉.

9. 확장 절차

새 보고서 종을 추가하는 일반 절차는 docs/handoff/frontend/report-summary-table.md §8(8단계)을 참고 — 본 문서(산출표)는 1단계(탭 등록)를 건너뛰고 ReportKindTabsAtoKINDS 배열에 항목만 추가해 만들어졌다(3단계 "데이터 컴포저블 신설"이 useCalculationTable, 4단계가 이 MainOrg.vue).