다크모드
청구 분개 — 성격별 대변 + 과세 VAT (G3b, as-built 2026-06-20)
규칙
- E2 청구확정 분개 대변은 성격(component)별 계정으로 분리한다.
- 매출 3구분(과세원금/면세원금/영세원금) → 단일 용역매출 0412(KcLep 표준, 합산 1행).
- 도메인 성격(장기수선충당금·예비비적립금) → 활성 프리셋이 공급한 계정.
- 부가세예수금(0255) = 과세원금 × 10%만. 면세·영세·부채(장충)·자본(예비비) 제외.
- DR 미수관리비(0108) = 공급대가 = Σ(성격별 공급가액) + 과세 VAT. 차·대 균형 불변식.
- 라인 순서: 미수(0108) → 용역매출(0412) → 장기수선충당부채(9112/9113) → 예비비적립금(9201) → 부가세(0255, 과세원금>0).
프리셋-스코프 (성격→계정 매핑)
- 성격(natures)은 공통. 성격→계정 매핑만 프리셋이 분기.
- 범용(항상):
billingNature.UNIVERSAL_COMPONENT_CREDIT— 매출→0412. 부가세 0255·미수 0108은useBillingJournal상수. - 도메인(프리셋):
billingPresets.BILLING_PRESETS[preset].domainCredit.- 공동주택(국토부고시 2023-300): 장기수선충당금→9112 장기수선충당금, 예비비적립금→9201 예비비적립금.
- 집합건물(법무부고시 2021-218): 수선충당 성격→9113 수선적립금(비유동부채 제19조). 예비비 성격→9203 임의적립금(자본 적립금 — 별표 전사로 확정, 집합건물엔 예비비적립금 계정 부재. 9201 준용 폐기). 전사 정본:
docs/REFERENCE-AGGREGATE-BUILDING-COA-2021-218.md.
- 활성 프리셋 =
useBillingPreset(reactive 싱글톤 + localStorage). 시스템 기본 = 없음(null, 도메인 비활성·범용만). 데모 레포 = 공동주택 시드. - 미선택(null)+도메인 성격 존재 시: 미매핑 안전 폴백 라인(accountCode '', accountName '미매핑(프리셋 미선택)')으로 차대균형 보존. 데모(공동주택)에선 무발생.
도메인 9xxx GL 정책(불변식 완화)
toCore: coreMapping 있으면 core(0xxx) 해소, null이면 9xxx 유지(도메인-고유). 무변경.- 9112/9113/9201은 coreMapping null → 분개·GL에 9xxx로 등장(허용).
- 누수 불변식: coreMapping 보유 9xxx(예 9001→0108)는 0건(여전히 해소). 완화는 도메인-고유로 한정.
경계 (G3b 비범위)
- E4(수납):
DR 보통예금 / CR 미수관리비(0108)통합 유지. 부가세는 E2 발생주의 인식, E4 미접촉. - 조정 per-구분 VAT: 에누리(discount)는 성격분해 완료 + 부가세 충당 정합(아래 §매출에누리 2026-06-22 → 2026-06-26 T1 정정). 대손(writeOff)은 부가가치세 대손세액공제 미적용(코어 외). 면제(lateFeeWaiver)는 연체료 무VAT.
- 6뷰
['service-charge']하드코딩 → 활성 프리셋 연동: 후속(일관성 작업).
검증된 데모 분해
| 대상 | byComponent | DR 미수 | CR 0412 | CR 9112 | CR 0255 |
|---|---|---|---|---|---|
| 홍길동 c-hong | 과세 8250·면세 3750·장충 3000 | 15,825 | 12,000 | 3,000 | 825 |
| 김철수 c-kim | 과세 2750·면세 1250·장충 1000 | 5,275 | 4,000 | 1,000 | 275 |
재무제표 영향 (G3b 필연적 귀결, 구현 중 확정)
G3b로 장기수선충당금이 매출(0412)이 아닌 부채(9112)로 대변 인식되면서 재무제표가 정합하게 바뀐다(기존 테스트는 G3b 이전 버그값을 인코딩했었음):
useFinancialStatements.jsMETA 맵 증강: 도메인-고유(coreMapping null) 계정(9112 등)을 BS 분류 대상에 추가 — 9112가 부채(LIABILITY)로 잡혀 BS가 균형을 이룬다.- 데모 검산(당기): 공급 20,000 = 과세 11,000 + 면세 5,000 + 장충 4,000. VAT = 과세 11,000 × 10% = 1,100. 미수(자산) = 21,100.
- 손익계산서 매출 = 과세 11,000 + 면세 5,000 = 16,000(장충 제외).
- 부채 = 장기수선충당금 9112 4,000 + 부가세예수금 0255 1,100 = 5,100.
- 자본(순이익) = 16,000. BS 균형: 자산 21,100 = 부채 5,100 + 자본 16,000.
- G3b 이전(버그): 매출/순이익 20,000(장충 포함), 미수 22,000(VAT를 전체 20,000의 10%=2,000으로 과대), 부채 2,000.
- 영향 테스트:
useFinancialStatements.spec.js·useClosing.spec.js기대값을 위 정합값으로 갱신(Task 4 커밋 f11317eca 포함).
2026-06-22 — 세구분 분류 레이어는 표시 전용(GL 불변)
수납 도출근거의 세구분 표를 "매출 / 비매출(부채·자본)"로 묶는 분류 레이어(billingNature.componentGroup/groupByComponentNature)를 추가했으나, 이는 표시·집계용 롤업일 뿐 GL 전기에 영향 없음:
componentOf(컴포넌트 키 = 성격)·componentCredit(성격→특정 계정)·CREDIT_COMPONENT_ORDER(대변 방출 순서) 전부 불변. GL은 여전히 항목별 특정 계정(장기수선충당부채 9112·예비비적립금 등)으로 전기한다.- 분류는
BILLING_NATURE[nature].group에서 파생 → 새 부과항목(예: 하자보수충당금 group:부채)도 코드 변경 없이 비매출·부채로 분류·롤업. 단 GL 대변계정은 프리셋(useBillingPreset.componentCredit)에 신규 성격→계정 매핑을 추가해야 한다(분류 레이어와 별개). - 부가세는 표시상 매출 그룹(공급대가 관례)이나 GL은 예수금 0255 — 분류 ≠ 계정.
2026-06-22 — 세금계산서 공급가액 = isVatable 캐논 (감사 결함 K)
useAllocations.lineTotalsTaxable(세금계산서 공급가액 집계)가 charge.taxCategory === '과세' 단독으로 과세 charge를 판정하던 것을 캐논 isVatable(charge)(= natureGroup(nature)==='매출' ∧ taxCategory==='과세')로 교체. 부채(장기수선충당금)·자본(예비비)이 taxCategory: '과세'로 오설정돼도 매출 공급가액에 섞이지 않는다(비매출은 세금계산서 대상 아님). isVatable은 billingNature.spec.js에서 {nature:'장기수선충당금', taxCategory:'과세'}→false 검증됨.
2026-06-22 — 매출에누리(discount) 성격분해 + VAT 정정 (감사 결함 B)
에누리(원금 감면) 분개가 감면 전액을 과세 단정(amount/1.1)하던 결함 수정. 면세(수도료)·영세 감면에 가공 부가세예수금이 계상되던 GL 왜곡 해소. 설계: docs/superpowers/specs/2026-06-22-discount-vat-nature-split-design.md.
- breakdown에 성격 적재:
useCollectingAllocation.allocateToComponent가 차수당 합산하던 것을 (차수×성격)별 엔트리{ period, component, amount }로 방출.component는 충당된 성격 키(과세원금/면세원금/영세원금/principal/lateFee 등).useForwarding.adjust는 period별 누적이라 다중 엔트리 안전. - 분개 규칙(
useBillingJournal.vouchersAdjustmentsdiscount 분기):taxableReduced= Σ(breakdown 中component==='과세원금').vat=round(taxableReduced − taxableReduced / (1 + vatRateAt(at)))— 과세분 VAT만. VAT율은billingNature.vatRateAt(거래일)SSOT(effective-dated, 하드코딩1.1제거).supply(매출에누리 0402 차변) =amount − vat(면세·영세분은 supply 전액).- 라인:
DR 매출환입및에누리(0402) supply / DR 부가세예수금(0255) vat(>0) / CR 미수관리비(0108) amount. 차·대 균형 불변(supply + vat = amount).
- 폴백: breakdown에
component가 없으면(수동/레거시 데이터)taxableReduced=0→ VAT 0(가공 VAT 미계상). 실경로는 항상component보유. - 검산: 과세 4000·면세 1000 혼합 감면(5000) → 부가세 364 / 매출에누리 4636. 순수 면세 감면 → 부가세 라인 부재. 전액 과세(1100) → 부가세 100 / 매출에누리 1000.
- 테스트:
useCollectingAllocation.spec.js(breakdown 성격분리·spill)·useBillingJournal.spec.js(혼합·순수면세·회귀).
2026-06-26 — 매출에누리 부가세 충당 정정 (3차 결함 T1) ⚠️ 위 2026-06-22 분개 규칙 대체
문제: 위 2026-06-22 수정은 breakdown.과세원금을 공급대가(VAT 포함)로 가정해 vat=round(x−x/1.1)로 역산했으나, G3a 이후 라이브 데이터에서 과세원금=공급가액(supply, VAT 제외)이고 부가세는 별도 구성요소다. 그 결과:
allocateToComponent('principal')이 부가세 컴포넌트를 충당 대상에서 제외 → 공급대가 감면 시Σbreakdown < a.amount(부가세분 누락).- 분개 CR 미수=
a.amount(공급대가)인데useForwarding.adjust는Σbreakdown(공급가액)만 차감 → 미수원장↔GL 영구 drift(차액=부가세). 부가세 outstanding도 영구 잔존(과세 라인 완납 불가).
수정(fix(billing) 3차 T1):
useCollectingAllocation.allocateToComponentdiscount('principal') 대상에 부가세 포함(billingNature.COMPONENT_SALES= {과세원금·부가세·면세원금·영세원금} SSOT). 부채/자본(장충·예비비)은 매출에누리 대상 아님(제외 유지).- 분개 VAT = breakdown 부가세 엔트리 직접 합산(
Σ component==='부가세') — 과세원금 공급대가 역산 폐기. 정확·Σbreakdown=a.amount보장 → forwarding↔GL drift 0.supply=amount−vat. CollectingActionOrg감면 입력을 매출 outstanding으로 clamp(부채/자본 침범 차단 → 입력 초과 시에도 정합 유지).- breakdown 의미 정정:
과세원금=공급가액(supply)·부가세=별도 엔트리. 분개는과세원금+부가세둘 다 매출 환입의 일부(과세원금→0412, 부가세→0255). - 폴백: breakdown에 부가세 엔트리 없으면 VAT 0(레거시·순수 면세).
- 검산(라이브): c-hong 이월 3차수 각 {과세원금 4000·부가세 400·면세원금 6000} 전액 감면(31,200) → 부가세 1,200·매출에누리 30,000·CR 미수 31,200, 이월 미수 완전소멸·당기 불변·drift 0.
- 테스트:
useBillingJournal.spec.js통합테스트(라이브 결합, 손주입 단위테스트 거짓통과 대체) + 단위테스트를 G3a 정합 모델(부가세 별도 엔트리)로 정정.CollectingActionOrg.spec.jsclamp.
2026-06-26 — 일괄 전기 마감 게이트 (3차 결함 T2)
- 문제: 단건 전기(
BillingIntakeOrg.confirmPost)는closing.canPost게이트하나, 일괄 전기(postAllUnposted)는 무게이트 → 마감기간 명목전표 사후전기로 0377 미처분이익잉여금 정합 파괴. 1차 결함 E(canPost 신설)가 단건만 고친 잔여. - 수정:
useGeneralLedger.postAllUnposted(gate?)선택적 predicate 수용({posted,skipped}반환).useClosing의존 미주입(순환 회피) — 게이트는 호출부가 주입.BillingIntakeOrg.confirmPostAll이(v) => closing.canPost(v).allowed주입. 단건/일괄 동일 게이트. 테스트:useGeneralLedger.spec.js·useClosing.spec.js.