다크모드
산출표 집계 계약 — BE 참고
관리비 상용화 보고서 확충 트랙(2026-07-11, B-1) — 산출표(신규). 설계 정본:
docs/superpowers/specs/2026-07-11-period-tag-minors-reports-xlsx-design.md§B-1. as-built 정본 소스:src/composables/useCalculationTable.js.
1. 개요
산출표는 이번 차수(당기)에 활성 상태인 관리비 항목 각각이 어떤 수량·단가로 얼마나 발생하고 할인·조정을 거쳐 최종 얼마로 부과됐는지를 항목 축 하나로 보여주는 read-only 문서다. 총괄표·부과내역서·수납현황이 세대(유닛) 축 중심이라면, 산출표는 부과 콘솔(설정 > 항목, useCharges)의 계산 결과를 그대로 항목별로 나열한다. 재계산은 일절 하지 않는다 — useCharges.amountsOf()가 이미 산출한 발생/부과 원금·단가를 그대로 재투영할 뿐이다.
2. 데이터 정본 — useCalculationTable
src/composables/useCalculationTable() → { calculationRows, calculationTotals, formatAmount }.
내부적으로 useCharges(activeCharges·amountsOf·parseAmount·formatAmount) 하나만 소비한다.
2-1. calculationRows() — 활성 항목 1행씩
js
function calculationRows() {
return activeCharges.value.map((c) => {
const a = amountsOf(c);
return {
chargeCode: c.chargeCode,
name: c.name,
chargeBasis: c.chargeBasis,
수량: parseAmount(c.targetCount),
단가: a.unitPrice,
발생금액: a.accruedPrincipal,
할인금액: parseAmount(c.chargeDiscount),
부과금액: a.chargePrincipal,
};
});
}수량/단가/발생금액/부과금액전부useCharges.amountsOf(charge)(SC-3a 4-addend 모델 — 발생측·부과측 각각 기본+할증+할인+조정 signed addend 합)의 파생값을 그대로 복사한다. 새 산식 없음.할인금액만 예외적으로amountsOf의 파생값이 아니라 부과측 입력(chargeDiscount) 원자값을parseAmount로 직접 뽑는다 —amountsOf는 4개 addend를 이미 합산해chargePrincipal만 반환하므로, 할인 하나만 별도 열로 노출하려면 addend를 원자값에서 직접 읽어야 한다(chargePrincipal자체는 재계산하지 않고 그대로 씀).
2-2. calculationTotals(rows)
js
function calculationTotals(rows = calculationRows()) {
const sum = (k) => rows.reduce((s, r) => s + r[k], 0);
return { 발생금액: sum("발생금액"), 할인금액: sum("할인금액"), 부과금액: sum("부과금액") };
}rows 생략 시 내부에서 calculationRows()를 재호출(총괄표 columnTotals 관용과 동일) — FE 소비 시 rows를 computed로 감싸 명시 전달 권장.
3. 불변식 3종 (vitest, src/composables/__tests__/useCalculationTable.spec.js)
| # | 불변식 | 지키는 회계 정합 |
|---|---|---|
| ① | 각 행 발생금액 === amountsOf(c).accruedPrincipal · 부과금액 === amountsOf(c).chargePrincipal · 단가 === amountsOf(c).unitPrice | 산출표가 부과 콘솔(useCharges)과 다른 계산 경로를 타지 않는다 — 두 화면 대조 시 항상 일치 |
| ② | Σ부과금액 === useChargeSummaryTable().columnTotals().세전합계 + useAllocations().totalUnallocated() | 항목축(산출표)과 유닛축(총괄표) 두 서로 다른 투영이 배분 잔차까지 포함해 항등 — 배분 로직이 두 화면 사이에서 값을 잃어버리지 않는다는 교차검증(독립 소스 대조 — 총괄표·useAllocations 둘 다 재사용, 자기순환 아님) |
| ③ | calculationTotals(rows).발생금액/부과금액 === rows.reduce(...) | 합계 열이 실제 행 합과 일치(열거합 회귀 가드) |
as-built 진단(2026-07-11): 3개 불변식 모두 최초 구현에서 통과 — amountsOf를 그대로 복사하는 얇은 재투영 레이어라 새 계산 경로가 없기 때문.
4. 필드 계약
| 필드 | 정의 |
|---|---|
chargeCode / name | 부과항목 식별(useCharges) |
chargeBasis | 배분기준(예: 사용량·전용) — 표시용, 배분 자체는 useAllocations가 별도 수행 |
수량 | parseAmount(c.targetCount) — 항목 설정(설정 > 항목)의 대상 수량 입력 원자값 |
단가 | amountsOf(c).unitPrice = targetCount ? Math.round(chargePrincipal/targetCount) : 0 |
발생금액 | amountsOf(c).accruedPrincipal — 발생측 4-addend 합(기본+할증+할인+조정) |
할인금액 | parseAmount(c.chargeDiscount) — 부과측 할인 addend 원자값(음수 부호 규약, §2-1) |
부과금액 | amountsOf(c).chargePrincipal — 부과측 4-addend 합 |
5. 스코프 제외
- 조정명세서·대장: 항목 조정(가감) 이력을 별도 문서로 감사 추적하는 조정명세서·조정대장은 이 트랙 스코프 밖이다. 산출표는 "현재 확정된 값"만 보여주며 조정 이력(누가·언제·왜 바꿨는지)은 다루지 않는다.
- 기간범위·층/블록 축: 이번 차수 하나·단지 전체 기준만 제공. 여러 차수 비교나 층/블록별 필터는 미구현(FE handoff §확장 포인트 참고).
6. 확장 포인트
- 조정명세서: 별도 컴포저블 신설 필요 — 항목 조정 입력(
chargeAdjustment/accruedAdjustment)의 변경 이력을 감사 로그로 남기는 기능이 선행돼야 한다(현재useCharges는 최신값만 보유, 이력 없음). - 기간범위 조회: 여러 차수(연월)의 산출표를 나란히 비교하려면
useBillingPeriod().periods(차수 이력)를 순회하며 각 차수 시점의useCharges스냅샷이 필요 — 현재useCharges는 차수와 무관한 단일 최신 상태만 갖고 있어(체인 개념 없음), 이 확장은useCharges자체의 차수별 스냅샷 구조 도입이 선행돼야 한다(중대한 변경, 별도 트랙 필요).