Skip to content

FE 메인테이너 — 도출근거 중첩 시트 패턴 (숫자 클릭 → 분개/T계정·계산 표)

정본 패턴(SSOT): leysys-design docs/decisions/PATTERN-DERIVATION-NESTED-SHEET-2026-06-21.md. 오너 결정(2026-06-21): 파생 숫자의 근거는 인라인 펼침·서술이 아니라 클릭 → 중첩 시트에 표로. 차변=좌·대변=우. 전 도메인 1패턴.

무엇을 푸는가

ERP 상세에서 파생 숫자(원금·연체료·미수·세액)의 "왜 이 값?"을 보여주던 두 방식이 거부됨:

  • 인라인 펼침 테이블(메인과 동일 크기 → 위계 구분 불가, 무엇의 근거인지 직관 안 됨)
  • 서술식 행("원금 세구분 — 과세 4,000 · 부가세 400 …" → 스캔 불가)
  • 부수적으로 펼침 쉐브론이 카드마다 좌/우 불일치.

파생 숫자를 <button>(클릭 어포던스)로, 클릭 시 중첩 시트에 표로 근거 표시. 쉐브론·인라인 펼침·서술 전부 폐기.

핵심 파일

파일역할
src/composables/useDerivation.js공유 상태 selectedDerivation(ref) + openDerivation(model) + 모델 빌더 periodPrincipalDerivation(period)(분개 T계정)·periodLateFeeDerivation(period)(계산+결과분개). 도메인 비의존.
src/components/billing/_core/console/overlays/SheetDerivationArt.vue중첩 도출근거 시트(id="sheet-derivation-art"). kind:'journal'→차/대 2단 T계정, kind:'computation'→계산표+결과분개.
…/collecting/detail/blocks/AllocationStatusOrg.vue충당현황 — 원금/연체료 셀이 data-derive-principal/data-derive-latefee 버튼.
…/collecting/detail/blocks/CarriedInstallmentsOrg.vue이월미납 — 동일. 인라인 펼침·쉐브론 컬럼 제거.
…/collecting/detail/blocks/SheetCollectingDetailArt.vue부모 시트. <SheetDerivationArt /> 1개 마운트(DialogCollectingCancelArt 옆).
…/late-fee/overview/overlays/SheetLateFeeDetailArt.vue연체료 드릴다운 — 차수 연체료 클릭 → 계산표. <SheetDerivationArt /> 마운트.

모델 계약 (useDerivation)

js
// 분개/T계정 — 회계 잔액(원금·미수·세액)의 근거
{ kind:'journal', title, subtitle?, debit:[{label,amount}], credit:[{label,amount}] }
// 계산 — 공식 파생(연체료 등). journal 있으면 결과 분개로 덧붙임
{ kind:'computation', title, subtitle?, columns:[{key,label,align?}], rows:[{...}],
  resultLabel, result, journal?:{debit,credit} }
  • periodPrincipalDerivation(period): period.byComponent(없으면 principalByTax) → 대변 구성. 차변 = 미수관리비(합). 계정 라벨 맵 CREDIT_ACCOUNT(과세원금→용역매출(과세) 등).
  • periodLateFeeDerivation(period): period.lateFee.breakdown(구간·요율·일수·금액) → 계산표 + 결과분개(DR 미수 / CR 연체료수입). Math.round로 소수 표시 정정.

새 도메인에 적용하는 법 (전파 가이드)

  1. 근거 있는 숫자 셀을 <button class="font-medium cursor-pointer hover:underline" command="show-modal" commandfor="sheet-derivation-art" @click="openDerivation(model)">로. 0/근거없음은 <span>.
  2. 모델은 빌더 재사용 또는 직접 구성(분개면 {kind:'journal',debit,credit}, 계산이면 {kind:'computation',columns,rows,result,journal?}).
  3. 부모 시트 안에 <SheetDerivationArt /> 1개 마운트.
  4. 차변 좌·대변 우 규약 준수(빌더가 보장). 신규 CSS 금지(sheet/table/card 조합).

전파 체크리스트 (전 도메인)

  • [x] 수납 상세 — 이월미납(CarriedInstallmentsOrg) 원금·연체료
  • [x] 수납 상세 — 충당현황(AllocationStatusOrg) 원금·연체료
  • [x] 연체료 현황 드릴다운(SheetLateFeeDetailArt) 차수 연체료
  • [ ] 회계 전표 상세(SheetReadBillingVoucherArt) — 분개 라인 → T계정/세구분
  • [ ] 세금계산서 — 과세/면세/영세 구분 도출
  • [ ] 결산·예산(budget MainOrg) — 집계 과목 → 구성 도출근거
  • [ ] 청구/수납 리스트 DynamicTableOrg(invoicing·collecting) 행 펼침 → 패턴 통일 검토(현 toggleExpand 잔존)
  • [ ] CollectingActionOrg 충당결과 세구분 행 — 패턴 통일 검토

테스트

  • __tests__/CarriedInstallmentsOrg.spec.js · AllocationStatusOrg.spec.js: 클릭 → selectedDerivation 모델(kind·debit/credit·rows) 검증. jsdom은 Invoker Commands 미구현이라 시트는 안 열리나 @click 핸들러는 실행 → 모델 단언으로 검증.
  • 시각: scripts/probe-derivation-sheet.mjs(Playwright) — 모바일 뷰포트로 원금→T계정·연체료→계산표 캡처.

주의

  • 중첩 <dialog showModal> 스택(부모 시트 위) — 콘솔 선례(DialogCollectingCancelArt). top-layer 자동 스택.
  • 도출근거 시트 폭 = sheet-width-md(부모 3xl보다 좁게 — 데스크톱서 뒤 시트 비침). 모바일은 전체 덮음(정상).
  • SheetDerivationArt는 빌링 overlays에 위치하나 도메인 비의존 — 전파 시 공용 위치로 승격 가능.

2026-06-21 라이브 리뷰 갱신 (운영관점 재설계 + 시간축 + 충당내역 + 용어)

오너 라이브 리뷰로 수납 상세를 운영관점으로 재설계. 회계 분개(T계정)는 수납 화면에서 제거(회계 전표 도메인 전용으로 유보).

  • 도출근거 운영 kind: useDerivation 빌더 재작성 — periodPrincipalDerivationkind:'breakdown'(부과액−기납부−감면=미수 + 미수 세구분 + 충당내역(payments) + 법정충당 안내), periodLateFeeDerivationkind:'latefee'(구간별 대상원금 컬럼 + 연이율 + 산식, 분개 제거). SheetDerivationArt가 두 kind 렌더(journal/computation 미사용).
  • 단일 차수별 표: CarriedInstallmentsOrg 삭제, AllocationStatusOrg = "차수별 미수·수납"(월분·납기·부과액·수납·미수금·연체료·받을금액). 표 w-max(콘텐츠 폭) + 카드 overflow-x-auto(모바일 가로 스크롤). 부과액/연체료 숫자 클릭 → 도출근거.
  • 시간축 케이스: useForwarding 확장 — 당기 부과 없는 계약(이월만)도 행 생성(hasCurrent 플래그, prior-only 계약 append, 3축 대칭). 상태 배지 정상/정산대기(detail.identity.billingStatus). 데모: 정산대기 세대(c-end, 당기 없음·장기연체 3단계), 중단·재개 세대(c-gap, 차수 공백).
  • 차수별 납기: 납기 = 익월 dueDay일(기본 5, 시드 period.dueDay로 차수별 변동). 연체료는 각 차수 납기 기준. useForwarding.nextMonthDay.
  • 부분납부 연체료 정합: useLateFee가 영수증(appliedBreakdown 일자별)을 연체료 엔진 payments로 전달 → 납부일 기준 구간 분할(대상원금 변동). 원금 = 부과액−감면.
  • E3 정합: vouchersLateFeeAccrualforwardingByContract 기반(정산대기 연체료도 GL 반영).
  • 용어 툴팁: composables/glossary.js(용어집) + components/common/TermTip.vue(DS .tooltip hover). 표 헤더·합계 라벨에 적용.

2026-06-22 갱신 — 세구분 분류 레이어(매출/비매출:부채·자본)

오너 지적: 장기수선충당금·예비비적립금을 세구분 표에 단독 항목으로 노출하면 범용성이 떨어진다(각각 부채·자본의 한 인스턴스). → 분류(매출/부채/자본) 롤업 레이어 추가. 특정 항목을 성격 카테고리로 묶어 표시.

  • billingNature.js 분류 레이어(신규, GL·componentOf 불변):
    • componentGroup(component)'매출'|'부채'|'자본'. 매출 세분(과세/면세/영세·부가세)은 매출, 그 외 성격명 키는 BILLING_NATURE[component].group. 미등록은 매출 폴백.
    • groupByComponentNature(items)[{group, label, rows, billed, paid, outstanding}](COMPONENT_GROUP_ORDER 순, 빈 그룹 제외). COMPONENT_GROUP_ORDER=['매출','부채','자본'], COMPONENT_GROUP_LABELS={매출:'매출', 부채:'비매출 · 부채', 자본:'비매출 · 자본'}.
    • 부가세는 본질상 예수금(부채)이나 공급대가 관례로 매출 그룹에 묶어 표시(오너 멘탈모델 일치). GL은 별도 0255 예수금 계정.
  • useDerivation.js: periodPrincipalDerivationcomponents(평면, 각 항목에 component 키)에 더해 componentGroups(분류 롤업) 방출.
  • SheetDerivationArt.vue: 세구분 표를 분류별 <tbody> 섹션으로 — 그룹 헤더 행(라벨+소계, bg-neutral-subtle font-medium) + 항목 행(pl-4 들여쓰기). 헤더 안내 "세구분 — 부과 → 충당 → 미수 (매출 / 비매출)".
  • 범용성: 새 부과항목(예: 하자보수충당금 group:부채)이 등록되면 코드 변경 없이 비매출·부채로 자동 분류·롤업. 검증: scripts/probe-derivation-sheet.mjs(당기 차수 → 매출+비매출·부채 그룹 + 장기수선충당금 항목).
  • 테스트: billingNature.spec.jscomponentGroup/groupByComponentNature/COMPONENT_GROUP_ORDER 단언 추가.

잔여(전파)

  • 충당순서 설정 UI(setting/collecting)도 동일 분류로 그룹핑 + 정책 구간 드래그 정렬(법정 연체료→원금 잠금) — 다음 트랙(오너 ②).
  • 연체요율 설정(12/24/36/48%) ↔ 엔진(3/4/5%) 목업 불일치 와이어링 — 별도 트랙.
  • 회계 전표(분개 T계정 archetype)·세금계산서·결산 근거에 도출근거 패턴 전파.
  • 정산대기 unit/member 축은 contract 축과 동일 확장 완료(3축). 신규 콘솔/이관 정산(전기이월)은 별도 트랙.
  • 매뉴얼(collecting-cases) 예시 숫자·스크린샷 단지 규모 리프레시(누적 미반영).