Skip to content

FE 핸드오프 — 연체료 발생 중지 설정 (2026-06-25)

독자: FE 메인테이너/AI. 연체료 발생 중지(accrualPause) 기능의 UI 배선·컴포넌트 구조·데이터 흐름·테스트 위치·확장 포인트를 정의한다. as-built: 브랜치 worktree-late-fee-pause (2026-06-25). BE 계약: docs/handoff/backend/late-fee-accrual-pause.md


1. 진입 경로 (라우트)

관리비 앱 → 관리비설정 → 연체료 할증 (/service-charge/setting/surcharge).

  • 라우트 세그먼트: lineLevel['setting/surcharge']IndexView.vueMainOrg.vue + overlays/SheetUpdateSurchargePause.vue.
  • 연체료 할증 페이지는 연체료 기본(인식기준)·연체요율·발생 중지 세 카드를 모두 포함한다.

2. 컴포넌트 구조

service-charge/setting/surcharge/
  MainOrg.vue                           — 읽기 바인딩 (발생 중지 카드 표시)
  overlays/
    SheetUpdateSurchargePause.vue       — 편집 시트 (read↔edit 토글 + 저장)

2-1. MainOrg.vue — 읽기 카드

파일: src/components/service-charge/setting/surcharge/MainOrg.vue

js
const { policy } = useLateFeePolicy();
const accrualPause = computed(() => policy.value.accrualPause);
  • accrualPause.enabled → 카드 요약 배지("사용" / "미사용")
  • accrualPause.startDay, accrualPause.endDay → 기간 표시 행

편집 버튼: command="show-modal" commandfor="sheet-update-surcharge-pause"SheetUpdateSurchargePause 오픈.

2-2. SheetUpdateSurchargePause.vue — 편집 시트

파일: src/components/service-charge/setting/surcharge/overlays/SheetUpdateSurchargePause.vue

sheet id: sheet-update-surcharge-pause sheet 폭: sheet-width-lg

Script setup 핵심

js
const { policy, setAccrualPause } = useLateFeePolicy();
const isEditMode = ref(false);
const draft = ref({ isUsed: "inactive", startDay: 10, endDay: 20 });

const seedDraft = () => {
  const p = policy.value.accrualPause;
  draft.value = {
    isUsed: p.enabled ? "active" : "inactive",
    startDay: p.startDay,
    endDay: p.endDay,
  };
};
seedDraft(); // 마운트 시 정책 → draft 동기화

const startEdit = () => {
  seedDraft();
  isEditMode.value = true;
};
const cancelEdit = () => {
  seedDraft();
  isEditMode.value = false;
};
const confirmEdit = () => {
  setAccrualPause({
    enabled: draft.value.isUsed === "active",
    startDay: draft.value.startDay,
    endDay: draft.value.endDay,
  });
  isEditMode.value = false;
};

draft ↔ 정책 매핑

draft 필드타입정책 필드변환
isUsed'active' / 'inactive'enabled (boolean)'active'true
startDaynumber (1–31)startDay그대로
endDaynumber (1–31)endDay그대로

폼 필드

  1. 발생 중지 기능<Select v-model="draft.isUsed"> (inactive / active), :disabled="!isEditMode"
  2. 발생 중지 기간startDay Select (1–31 일) + endDay Select (1–31 일), 두 드롭다운, :disabled="!isEditMode"

푸터 모드 토글

읽기 모드: [편집] → startEdit()
편집 모드: [취소] → cancelEdit() | [확인] → confirmEdit()

버튼 클래스: 편집 button-neutral-subtle button-md / 취소 button-neutral-subtle button-md / 확인 button-primary-bold button-md.


3. 데이터 흐름

lateFeePolicy.js
  policy (ref, singleton)
    └── .accrualPause { enabled, startDay, endDay }

          ├── [읽기] MainOrg.accrualPause (computed) → 카드 표시

          └── [편집] SheetUpdateSurchargePause
                seedDraft(): policy → draft (isUsed, startDay, endDay)
                confirmEdit(): draft → setAccrualPause() → policy 교체

setAccrualPause(partial)
  → { ...policy.value.accrualPause, ...partial } 검증(1≤start≤end≤31)
  → 유효 시 policy.value = { ...policy.value, accrualPause: next }

policy 변경 후 즉시:
  useLateFee.lateFeesFor()
    effectiveAsOf(asOf, policy.value.accrualPause)
    computeLateFees(installments, evalDate, ...)
  → 연체료 현황·수납 상세·분개 등 모든 소비처가 reactive로 재계산

취소(cancelEdit) 시: seedDraft()로 draft를 정책 현재값으로 되돌림 → 정책 불변.


4. 소비 컴포저블 요약

컴포저블용도
useLateFeePolicy()policy ref (읽기), setAccrualPause(partial) (쓰기)
effectiveAsOf(asOf, pause)interestEngine.js 직접 import순수 함수, useLateFee 내부에서 호출
useLateFee().lateFeesFor(axis, key, asOf)발생 중지 적용된 연체료 산출 (소비처: 수납 상세·현황 등)

5. 테스트 위치

파일커버 내용
src/composables/__tests__/interestEngine.spec.jseffectiveAsOf — 창 내 동결 7케이스 (startDay=1 전월말일, 창 이전/이후 통과, disabled 통과 등)
src/composables/__tests__/lateFeePolicy.spec.jsaccrualPause 정책 — setAccrualPause 부분갱신·검증 6케이스 (무효 no-op, 경계값 등)
src/composables/__tests__/useLateFee.spec.js통합 — 발생 중지 적용 시 lateFeesFor 연체료 동결 2케이스
src/components/service-charge/setting/surcharge/__tests__/SurchargePause.spec.jsUI 읽기 바인딩 2케이스 (enabled 상태 표시·startDay/endDay 렌더링)

6. 확장 포인트

6-1. 3계층 스코프 (❌ 예정)

현재 accrualPauselateFeePolicy.js 단일 싱글톤(로컬 스코프). 충당순서·연체요율처럼 프리셋/글로벌/로컬 3계층으로 분리하려면:

  1. DEFAULT_LATE_FEE_POLICY.accrualPause를 프리셋(frozen).
  2. globalAccrualPause ref 추가.
  3. setAccrualPause(partial, scope) — scope 분기.
  4. MainOrg에 스코프 토글 + provenance 배지(충당순서 §1-B-3 참조).
  5. 엔진은 항상 로컬 policy.accrualPause를 읽음(충당순서와 동일 패턴).

6-2. 일회성 날짜 구간 (❌ 예정)

현재 "매월 N일M일" 반복 창만 지원. 특정 날짜 구간 한 번(예: 2025-12-102025-12-20)만 중지하려면:

  1. accrualPause 구조에 type: 'monthly' | 'once' 필드 추가.
  2. effectiveAsOfonce 분기: asOf 자체가 구간 내면 동결.
  3. UI: 시트에 구간 타입 선택 + 날짜 피커 추가.

6-3. 엔진 무영향 원칙 유지

lateFeeOf, computeLateFees, overdueDays, rateSegments 시그니처는 변경하지 않는다. 중지 로직은 항상 effectiveAsOf에서 asOf를 필터링하는 방식을 유지한다.