다크모드
설정 스코프 — 백엔드 참고 (프리셋/글로벌/로컬 + 전파 클래스)
정본 ADR: docs/decisions/SETTINGS-SCOPE-GLOBAL-LOCAL-PRESET-2026-06-22.md. 본 문서는 BE 계약·불변식 요약.
3계층 + 전파 클래스
preset(코드 불변 권장값) ──기본──▶ global(조직 기본값) ──생성 시 스냅샷──▶ local(콘솔별)| 클래스 | resolve | 글로벌 변경 → 기존 콘솔 | 로컬 override | 적용 예 |
|---|---|---|---|---|
| frozen(기본) | local(생성 시 materialize) | 불변(스냅샷) | 허용 | 충당순서·인식기준·납기일·반올림 |
| live | global ?? preset | 즉시 동기(버전 없음) | 금지 | 세금구분·성격(BILLING_NATURE)·CoA |
| effective-dated | versionAt(key, txDate) | 날짜로 공존(소급 없음) | 금지 | 부가세율·연체 법정요율 |
| notify | local ?? global ?? preset + 배너 | opt-in | 허용 | (선택) 정책 롤아웃 |
구현 상태 (as-built)
- frozen — 충당순서(
lateFeePolicy.collectionPolicy): ✅PRESET_COLLECTION_POLICY+globalCollectionPolicy(ref) +policy.collectionPolicy(로컬).setCollectionPolicy(partial, scope)/resetCollectionPolicy(scope). 빌링 엔진은 항상 로컬. - effective-dated — 부가세율(VAT): ✅ (2026-06-22). 아래 §부가세 계약.
- effective-dated — rateSchedule(연체 법정요율): ✅ (2026-06-22). 아래 §연체요율 계약.
- live — BILLING_NATURE/CoA: ❌ 예정(현재 코드 const).
effective-dated 인프라 — effectiveDated.js
- 버전 모델:
[{ effectiveFrom: 'YYYY-MM-DD', ...payload }]. 신·구 값이 날짜로 공존. versionAt(versions, txDate)=effectiveFrom ≤ txDate중 최신(전부 미래면 가장 이른 버전 폴백). YYYY-MM-DD 사전식 비교.- 불변식: 과거 거래는 과거 effectiveFrom 버전을 선택 → 이미 굴러간 계산은 소급 변경되지 않는다(감사 추적). 글로벌에 미래 버전을 추가해도 과거 전표 불변.
부가세 계약 (billingNature)
PRESET_VAT_SCHEDULE = [{ effectiveFrom: '1977-07-01', rate: 0.1 }](한국 부가세 도입 후 10% 불변 → 단일 버전).vatRateAt(txDate)= 거래일 유효 부가세율. 모든 VAT 산출이 이 함수 경유(공급대가 = 공급가액 + 공급가액×vatRateAt(거래일)).- 소비:
withVat(byComponent, date),useBillingJournal(거래일 = 전기일 POSTING_DATE),useAllocations(세금계산서),useMoveOutSettlement·useProvisionalSettlement(정산). VAT_RATE= 현재 기준 편의값(=vatRateAt()) — 표시·하위호환. 엔진 계산은 거래일 기준vatRateAt(date).
- 소비:
setVatSchedule(versions)= 글로벌 개정(정규화). scope 인자 없음 = 로컬 override 불가(글로벌 전용).- BE 전환: txDate는 청구/전표 거래일. 부가세 개정 시
globalVatSchedule에{effectiveFrom: 개정일, rate: 신율}추가 → 개정일 이후 거래만 신율, 과거 무변. 멀티콘솔에서도 부가세는 콘솔별이 아닌 법정 단일(글로벌) — 콘솔 키 스토어에 두지 않는다.
연체요율 계약 (lateFeePolicy)
PRESET_RATE_SCHEDULE_VERSIONS = [{ effectiveFrom: '2000-01-01', schedule: [{fromMonths, annualRate}] }](데모 단일 현행 버전 — 모든 데모 차수에 적용).globalRateScheduleVersions(ref·글로벌 전용).rateScheduleAt(txDate)= 거래일 유효 tier-table.setRateSchedule(schedule, effectiveFrom?)= 글로벌 개정(effectiveFrom 미지정 = 현행 버전 교체). 로컬 override 없음.- 엔진 통합:
interestEngine.lateFeeOf가policy.rateScheduleVersions있으면 차수 납기일(dueDate) 기준 버전 선택(versionAt) → 그 채무에 적용되는 요율표. 버전 미제공이면 flatpolicy.rateSchedule폴백(레거시 호환).useLateFee가{...policy.value, rateScheduleVersions}주입. policy.rateSchedule은 현재 청구기 유효 단계표로 동기(표시·rateScheduleRows·하위호환). 엔진 계산은 거래일 버전.- 불변식: 각 tier-table 첫 구간
fromMonths=0필수. 데모는 단일 버전 → 결과 무회귀. - 후속: 달력 구간별 요율 선택(한 차수의 연체기간이 개정일을 걸치면 구간마다 다른 버전) — 현재는 납기일 단일 버전. 설정 UI 버전 이력(적용일 편집기)도 후속.
규칙3 (글로벌 변경 → 기존 콘솔 불변)
- frozen: 글로벌·로컬 독립 ref(라이브 바인딩 X) — 글로벌 변경이 로컬 스냅샷에 영향 없음(단위 테스트).
- effective-dated: 동기화가 아니라 거래일 버전 선택으로 과거 보존(force-sync 아님).
- 빌링 엔진은 frozen은 로컬, effective-dated는 거래일 버전을 읽음 — 글로벌은 신규 콘솔 시드/법정 정본 표면.