Skip to content

BE 참고 — 빌링 메뉴 IA 통합이전 + CoA 공유 인프라화

독자: BE 개발자/AI. 정본 스펙: docs/superpowers/specs/2026-06-20-billing-menu-ia-coa-consolidation-design.md as-built 기준: 2026-06-20 (브랜치 feature/billing-menu-ia-coa-consolidation, main 머지 예정)

이 문서는 CoA(계정과목) 3계층 소유권 모델, 활성 프리셋의 워크스페이스 엔티티 속성으로서의 의미(분개 영향 포함), CoA 데이터 SSOT 계약, always-open account-setting 게이팅 불변식, 구독 전이 회복력 규칙을 정의한다.

0. Workspace 메뉴 표시명 계약 (2026-07-16)

  • 판매·구독 상품의 정본 이름은 PRODUCT_MODULES[].name = '관리비'다.
  • Workspace 업무 메뉴와 홈 업무 카드에서는 행위 범위를 명확히 하기 위해 menuLabel = '관리비관리'를 사용한다.
  • 상품명·가격표·계약 데이터와 라우트 키 service-charge는 바뀌지 않는다. 따라서 백엔드의 상품 코드, 권한, 구독 판정에는 영향이 없다.
  • 메뉴 표시명을 서버 데이터나 상품명으로 역해석하지 않는다. 서버 계약은 계속 service-charge 키를 사용한다.

1. 3계층 소유권 모델

계층소유 주체가용성 조건역할
데이터 SSOT (useChartOfAccounts + 활성 프리셋)워크스페이스(엔티티)항상계정 데이터 정본. 어느 모듈 ON↔OFF에도 불변
계정과목 설정(저작 표면)회계 앱 account/generalalways-open (account-setting만 ungated; 그 외 회계 메뉴는 accountingEnabled)계정과목 등록·열람·커스터마이즈의 단일 표면
관리비설정 · 퀵검침service-charge 앱관리비 모듈 ONservice-charge 라인 config. 계정과목과 무관

핵심 원칙: CoA 저작은 관리비 분개(useBillingJournal)와 회계 장부(useGeneralLedger) 양쪽의 전제 인프라다. 회계 프리미엄으로 게이팅하면 분개 발생 전에 계정이 없어 무결성 오류가 생긴다. → always-open.


2. 활성 프리셋 — 워크스페이스 엔티티 속성

2-1. 무엇인가

useBillingPreset(싱글톤, localStorage leyve.billing.preset)이 관리하는 값. 현재 정의:

js
// 가능한 값
"공동주택"; // 국토부고시 2023-300
"집합건물"; // 법무부고시 2021-218
null; // 미선택(도메인 비활성 — 범용 계정만)

데모 레포 기본 = '공동주택'.

2-2. 분개에 미치는 영향

useBillingJournal.vouchersInvoicing()componentCredit()을 읽어 성격별 대변 계정을 결정한다.

프리셋장기수선충당금 대변 계정예비비적립금 대변 계정
공동주택9112 장기수선충당금9201 예비비적립금
집합건물9113 수선적립금9201 예비비적립금(준용)
null미매핑 안전 폴백(accountCode '')미매핑 안전 폴백
  • 미매핑 폴백은 차대 균형을 유지한다(accountCode 공백, accountName '미매핑(프리셋 미선택)'). 다운스트림 GL은 accountCode 공백 방어 처리 필요.
  • 프리셋은 분개 발생 시점의 값을 소비한다. 프리셋이 변경되면 이후 분개부터 적용된다(과거 분개 소급 없음 — 실서비스 주의).

2-3. 워크스페이스 소유의 의미

프리셋 = "이 단지(엔티티)가 공동주택인가 집합건물인가"라는 엔티티 고유 속성이다.

  • 실서비스 BE 구현 시: 프리셋은 워크스페이스 엔티티 테이블(예: workspace.billing_preset)에 저장한다. 현재 프로토는 localStorage에 저장하나 이는 단일 기기 데모 전용이다.
  • 어느 모듈(회계 OFF, 관리비 OFF)이 꺼져도 워크스페이스 엔티티에 붙어 있으므로 프리셋이 소멸하지 않는다.
  • 도메인 계정(9112·9113·9201)은 프리셋을 통해 CoA 데이터 SSOT에서 파생된다. 모듈이 꺼져도 CoA 데이터는 유지된다.

2-4. 네이밍 메모(후속 후보)

useBillingPreset / leyve.billing.presetbilling 스코핑은 워크스페이스 소유 의미와 어긋난다. 코드 리네임(useWorkspacePreset, leyve.workspace.preset 등)은 후속 작업으로 분류됨. 본 스펙 범위 밖.


3. CoA 데이터 SSOT — useChartOfAccounts

js
// src/composables/useChartOfAccounts.js
useChartOfAccounts(activePacks?: string[]) → accounts[]
  • 순수 함수(Vue import 0, 사이드 이펙트 없음).
  • CORE_ACCOUNTS(0xxx 범용 계정) ∪ PACKS[팩코드](도메인 계정) 합산.
  • activePacks가 없으면 코어만 반환한다.

팩 매핑 (현재 단일 팩, as-built)

js
// PRESET_PACK (as-built)
{
  '공동주택': 'service-charge',
  '집합건물': 'service-charge',
}
// → PACKS['service-charge'] = SERVICE_CHARGE_ACCOUNTS (9112·9113·9201 등 포함)
  • 현재 공동주택·집합건물이 단일 service-charge 팩을 공유한다. 도메인 별도 팩 분할은 후속.
  • 회계 account-setting 화면은 useChartOfAccounts([PRESET_PACK[활성프리셋]])으로 소비한다. 프리셋 미선택 시 코어만 표시.

불변식

  • useChartOfAccounts 함수 시그니처·반환 형태 무변경. 프리셋·팩 구조 변경은 이 함수 내부에서만.
  • 9xxx 도메인 계정의 coreMapping 필드: null이면 GL에서 9xxx 그대로 사용(허용). coreMapping이 있는 9xxx는 0건(누수 불변식).

4. always-open 게이팅 불변식

4-1. 계정과목 설정(account/general)은 항상 접근 가능해야 한다

상태계정과목 접근그 외 회계 메뉴 접근
회계 ON가능가능
회계 OFF가능불가(게이팅)
관리비 ON가능가능(회계 ON이면)
관리비 OFF가능가능(회계 ON이면)

4-2. 구현 위치 (as-built)

  • 워크스페이스 런처 (components/workspace/AppAsideOrg.vue): 회계 앱 그룹이 항상 노출됨. 그룹 내 서브메뉴 중 계정과목(account/general 딥링크)은 ungated. 그 외 서브메뉴는 accountingEnabled로 게이팅.
  • 회계앱 내부 사이드바 (components/accounting/AppSideNavMenuOrg.vue): account/general 항목만 ungated. 나머지 섹션(전표·원장·재무제표 등)은 accountingEnabled 게이팅.
  • service-charge 사이드바 "계정과목" 그룹: /service-charge/setting/account/general에서 같은 CoA SSOT를 소비한다. 관리비 셸을 유지하며 데이터·API 계약은 회계 계정과목과 동일하다.

4-3. 실서비스 구현 원칙

  • account-setting ungated 원칙은 API·라우트 가드에도 적용해야 한다. 계정과목 조회·등록 API는 모듈 구독 없이도 접근 가능해야 한다.
  • 세금계산서·전표·원장·재무제표 API는 accountingEnabled 체크 후 접근.

5. 구독 전이 회복력 규칙

세 계층(데이터 SSOT · 저작 표면 · 관리비설정)이 서로 hard dependency가 없어 어느 표면이 사라져도 나머지가 잔존한다.

전이 시나리오데이터 SSOT계정과목 저작 표면관리비설정 표면분개
회계 OFF → 회계 ON유지유지(always-open)유지복원
관리비 OFF → 관리비 ON유지유지유지복원
회계+관리비 동시 ON → 회계만 ON유지유지불노출(관리비 OFF)유지(도메인 계정 포함)
회계+관리비 동시 ON → 관리비만 ON유지always-open으로 유지유지분개 미리보기 불노출(accountingEnabled=false)

핵심: 프리셋이 워크스페이스 소유이므로 "회계만 ON" 상태에서도 도메인 계정이 account-setting에 계속 보인다. 6뷰 편의 표면(구 /billing/accounting-account/*)은 관리비와 함께 사라졌으나, 데이터와 저작 표면(회계앱)은 잔존한다.


6. 제거된 구조 (as-built)

6-1. 계정과목 6뷰 제거

  • 제거: /billing/accounting-account/{building,housing,property}/{balance-sheet,income-statement} 6라우트
  • 제거: views/billing/accounting-account/* · components/billing/accounting-account/*
  • 수렴: 회계 앱 account/general 단일 화면(프리셋 인지 + BS/IS 탭)

6-2. 관리비설정 라우트 이전

  • 이전 전: /billing/setting/* (7라우트) — 빌링 우산 소유
  • 이전 후: /service-charge/setting/* (7라우트) — service-charge 라인 소유(buildLineRoutes lineLevel 슬롯)

6-3. 빌링 우산 축소

  • components/billing/AppAsideOrg.vue: 대시보드·CMS·업무·관리비설정·계정과목 그룹 제거 → 빌링 타이틀 + hospitality 폴백만 잔존

7. 엣지케이스 / 주의사항

  • 프리셋 미선택(null) + 도메인 성격 charge: 분개 대변에 미매핑 안전 폴백 라인(accountCode '')이 생긴다. 차대 균형은 유지되나 GL 전기 불가 상태. 데모 레포(공동주택 시드)에선 무발생. 실서비스는 워크스페이스 생성 시 프리셋 강제 선택 또는 null 방어 로직 필요.
  • 프리셋 단일 팩: 현재 PRESET_PACK이 공동주택·집합건물을 모두 'service-charge' 팩으로 매핑한다. 도메인 별도 팩 분할(공동주택 팩/집합건물 팩) 시 이 매핑과 PACKS 객체를 함께 수정해야 한다. 엄격 도메인 필터(팩 분할 후 해당 단지 유형 계정만 표시)는 후속.
  • 연체료 기준: useLateFeeperiod.outstanding(공급대가, VAT 포함)을 연체료 base로 사용. VAT 제외 base가 필요하면 G3b/G4에서 설정.

8. 관련 파일

레이어파일
CoA 데이터 SSOTsrc/composables/useChartOfAccounts.js
활성 프리셋src/composables/useBillingPreset.js
분개 성격별 대변src/composables/useBillingJournal.js (vouchersInvoicing)
성격·계정 매핑src/composables/billingNature.js (UNIVERSAL_COMPONENT_CREDIT) · src/data/billingPresets.js (BILLING_PRESETS[preset].domainCredit)
라인 디스크립터src/data/billingLines.js (descriptor.sidebar 그룹 구조)
라우트 팩토리src/router/buildLineRoutes.js (lineLevel 슬롯)
게이팅src/composables/useModuleSubscription.js (accountingEnabled)
런처 사이드바src/components/workspace/AppAsideOrg.vue (always-open 게이팅)
회계앱 내부 navsrc/components/accounting/AppSideNavMenuOrg.vue (account-setting ungated)
회계 CoA 화면src/components/accounting/.../basic-setting/account/general/blocks/DynamicTableOrg.vue