test: E2E 수수료 정산 시나리오 테스트 + 엑셀업로드/분급 버그 수정
- 시나리오 문서: 보험 인입→수수료 지급 8단계 (docs/시나리오_E2E_보험인입_수수료정산.md) - SettlementScenarioTest(9건): 계약1건 생애주기를 실제 계산기로 단계별 검증 - ExcelServiceImportTest(2건): 실 .xlsx 업로드 라운드트립 검증 - fix(CRITICAL): 엑셀 업로드 런타임 깨짐 — xlsx-streamer 2.2.0 ↔ poi 5.2.5 비호환(NoSuchMethodError). excel-streaming-reader 4.3.0(유지보수 fork)로 교체. 영향: ExcelService.importLargeExcel + UploadService(/api/upload 실엔드포인트) - fix: 분급 계획 반올림 잔차로 분급합계≠원금(정산 무결성 위반). 비율합 100% 정규스케줄일 때 마지막 회차가 잔차 흡수. ga-api/ga-batch 양쪽. - 전체 build+test GREEN: 148건 0실패 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
# E2E 시나리오 — 보험이 어떻게 들어와서 수수료가 어떻게 나가는가
|
||||
|
||||
> 이 문서는 **계약 1건이 인입되어 설계사 통장에 돈이 꽂힐 때까지**의 전체 순서를
|
||||
> "등장인물 + 숫자"로 따라가는 시나리오입니다.
|
||||
> 각 단계는 실제 계산 코드(`CommissionCalculator`, `RegulatoryLimitChecker`,
|
||||
> `InstallmentPlanGenerator`, `ChargebackCalculator`, `TaxCalculator`)와 1:1로 대응하며,
|
||||
> 이 시나리오를 그대로 검증하는 단위테스트가 `SettlementScenarioTest` 입니다.
|
||||
|
||||
---
|
||||
|
||||
## 0. 등장인물 (이 시나리오의 테스트 데이터)
|
||||
|
||||
| 역할 | 식별자 | 속성 |
|
||||
|---|---|---|
|
||||
| 원 모집설계사 **김설계** | `agentId=10`, `gradeId=5` | 사업소득(BUSINESS_INCOME) · 과세사업자(TAXABLE) |
|
||||
| 후임 승계설계사 **박승계** | `agentId=20`, `gradeId=3` | (계약 승계 단계에서 등장) |
|
||||
| 상위 직책자 **이지점** | `agentId=30` | 오버라이드 수령 |
|
||||
| 상품 **무배당 종신보험** | `productId=1`, `WHOLE_LIFE`, 카테고리 `LIFE` | |
|
||||
| 계약 | `contractId=100`, `policyNo=POL-0001` | 연납보험료 **1,000,000원**, 계약일 **2024-01-15** |
|
||||
|
||||
기준 룰(테스트에서 Mock으로 주입하는 데이터):
|
||||
|
||||
| 룰 | 값 |
|
||||
|---|---|
|
||||
| 보험사요율(company_rate) 1차년 | 60% |
|
||||
| 보험사요율 2차년 | 10% |
|
||||
| 지급률(payout_rate) 등급5·WHOLE_LIFE·1차년 | 75% |
|
||||
| 지급률 등급5·2차년 | 50% |
|
||||
| 지급률 등급3(후임)·2차년 | 40% |
|
||||
| 분급 비율(7년) | 35 / 25 / 15 / 10 / 7 / 5 / 3 (%) |
|
||||
| 차등환수 13~24개월 구간 | 50% |
|
||||
| 1200% 한도(TOTAL_RATIO) | 12,000,000원 (연납 1,000,000 × 1200%) |
|
||||
| 원천세 | 사업소득 3.3% · 지방세 = 소득세 10% · 부가세 10%(과세사업자) |
|
||||
|
||||
---
|
||||
|
||||
## 1. 한눈에 보는 순서
|
||||
|
||||
```
|
||||
[보험사] 실적데이터 인입
|
||||
│ (Step1 receive → Step2 transform → Step3 reconcile)
|
||||
▼
|
||||
① 모집수수료 원장 계산 (Step4 calcRecruit) ← premium × company_rate × payout_rate
|
||||
│
|
||||
② 1200% 룰 한도 체크 (RegulatoryLimitChecker) ← 누적이 한도를 넘으면 잘라냄
|
||||
│
|
||||
③ 분급(7년) 계획 생성 (InstallmentPlanGenerator) ← 큰 모집수수료를 7년에 나눠 지급
|
||||
│
|
||||
④ 유지수수료 원장 계산 (Step5 calcMaintain) ← 2차년 이후 매년
|
||||
│ └ ⑤ 계약 승계 시 후임에게 귀속 전환 (모집은 원설계사 유지)
|
||||
│
|
||||
⑥ 오버라이드 계산 (Step7 calcOverride) ← 하위실적 × 율 (cap 제한)
|
||||
│
|
||||
⑦ 조기 해지 환수 (Step6 chargeback) ← 유지개월 짧을수록 많이 환수
|
||||
│
|
||||
⑧ 설계사×월 집계 (Step8 aggregate) ← gross 합산
|
||||
│ └ 세금 원천징수 + 공제 차감 → net(실지급액)
|
||||
▼
|
||||
[설계사 통장] ← 펌뱅킹 이체(KFTC)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 단계별 숫자 (시나리오 본문)
|
||||
|
||||
### ① 모집수수료 원장 계산 — 보험이 "들어오는" 첫 돈
|
||||
계약이 인입되면 설계사 김설계(등급5)의 모집수수료 원장 1줄이 생긴다.
|
||||
|
||||
```
|
||||
① 보험사지급액 = 보험료 1,000,000 × 보험사요율 60% = 600,000
|
||||
② 설계사수수료 = 600,000 × 지급률 75% = 450,000 ← gross
|
||||
③ 원장 원천세 = 450,000 × 3.3% = 14,850
|
||||
```
|
||||
→ 원장: `agent_id=10`, `commission_year=1`, `agent_amount=450,000`, `tax_amount=14,850`
|
||||
|
||||
### ② 1200% 룰 한도 체크
|
||||
초년도 모집수수료 누적이 연납보험료의 1200%(=12,000,000)를 넘으면 안 된다.
|
||||
- 현재 누적 0 + 450,000 = 450,000 ≤ 12,000,000 → **통과**
|
||||
- 만약 추가분이 한도를 넘기면 `ViolationResult.violated` 로 막고 초과분을 잘라낸다.
|
||||
|
||||
### ③ 분급(7년 분할) 계획 생성
|
||||
모집수수료 450,000을 한 번에 안 주고 7년에 나눈다.
|
||||
|
||||
| 차년 | 비율 | 지급액 |
|
||||
|---|---|---|
|
||||
| 1 | 35% | 157,500 |
|
||||
| 2 | 25% | 112,500 |
|
||||
| 3 | 15% | 67,500 |
|
||||
| 4 | 10% | 45,000 |
|
||||
| 5 | 7% | 31,500 |
|
||||
| 6 | 5% | 22,500 |
|
||||
| 7 | 3% | 13,500 |
|
||||
| **합** | **100%** | **450,000** |
|
||||
|
||||
> **무결성 규칙:** 분급액의 **합은 원수수료와 정확히 같아야 한다.**
|
||||
> 금액이 딱 나눠떨어지지 않을 때(예: 435,150원) 회차별 반올림 잔차가 생기는데,
|
||||
> 이때는 **마지막 회차가 잔차를 흡수**해서 합계를 원금에 맞춘다. (→ §4 보완)
|
||||
|
||||
### ④ 유지수수료 (2차년)
|
||||
계약이 유지되면 2차년부터 유지수수료가 매년 발생한다.
|
||||
```
|
||||
보험사지급액 = 1,000,000 × 10%(2차년 요율) = 100,000
|
||||
설계사수수료 = 100,000 × 50%(등급5 2차년) = 50,000
|
||||
```
|
||||
|
||||
### ⑤ 계약 승계 (김설계 해촉 → 박승계 후임)
|
||||
김설계가 해촉되면 그 계약의 **유지수수료만** 후임 박승계(등급3)에게 넘어간다.
|
||||
```
|
||||
유지수수료(후임) = 100,000 × 40%(등급3 2차년) = 40,000 → 원장 agent_id = 20(박승계)
|
||||
```
|
||||
- **모집수수료는 원 모집설계사 김설계(10)에게 그대로 남는다.** (고아계약 승계 표준)
|
||||
|
||||
### ⑥ 오버라이드 (상위 직책자 이지점)
|
||||
하위 김설계의 실적에 대해 상위 이지점이 받는 수당.
|
||||
```
|
||||
오버라이드 = 하위실적 2,000,000 × 4% = 80,000 (cap 100,000 미만이므로 그대로)
|
||||
```
|
||||
|
||||
### ⑦ 조기 해지 환수 — 13개월차 해지
|
||||
계약이 13개월차(2024-01-15 → 2025-02-15)에 깨지면 13~24개월 구간 환수율 50%.
|
||||
```
|
||||
환수액 = 기지급 모집수수료 450,000 × 50% = 225,000 ← 다음 정산에서 (−) 차감
|
||||
```
|
||||
|
||||
### ⑧ 집계 → 세금 → 실지급액(net)
|
||||
김설계 한 달치를 settle_master로 집계하고 세금·공제를 반영한다.
|
||||
과세사업자(TAXABLE) gross 450,000 기준:
|
||||
```
|
||||
소득세 = 450,000 × 3.3% = 14,850
|
||||
지방세 = 14,850 × 10% = 1,485
|
||||
부가세 = 450,000 × 10% = 45,000 (과세사업자만 +)
|
||||
실지급 = 450,000 − 14,850 − 1,485 + 45,000 = 478,665
|
||||
```
|
||||
- 여기에 가지급금 공제 100,000이 있으면 net = 478,665 − 100,000 = **378,665**
|
||||
- 비과세(NONE) 설계사는 부가세가 없어 net = 450,000 − 14,850 − 1,485 = **433,665**
|
||||
|
||||
---
|
||||
|
||||
## 3. 시나리오 ↔ 코드 ↔ 테스트 대응표
|
||||
|
||||
| 단계 | 코드 | 테스트 메서드 |
|
||||
|---|---|---|
|
||||
| ① 모집수수료 | `CommissionCalculator.calcRecruitLedger` | `s1_보험인입_모집수수료_원장계산` |
|
||||
| ② 1200% 룰 | `BatchRegulatoryLimitChecker.checkContractTotal` | `s2_규제_1200퍼센트_한도_통과및위반` |
|
||||
| ③ 분급 7년 | `BatchInstallmentPlanGenerator.generate` | `s3_분급_7년_분할_합계100퍼센트` |
|
||||
| ③ 분급 잔차 | 〃 | `s3b_분급_라운딩_잔차_마지막회차_흡수` |
|
||||
| ④ 유지수수료 | `CommissionCalculator.calcMaintainLedger` | `s4_유지수수료_2차년` |
|
||||
| ⑤ 계약 승계 | `CommissionCalculator.calcMaintainLedger(effectiveAgentId)` | `s5_계약승계_유지수수료_후임귀속` |
|
||||
| ⑦ 차등 환수 | `BatchChargebackCalculator.calculate` | `s6_조기해지_차등환수_13개월_50퍼센트` |
|
||||
| ⑧ 세금/실지급 | `BatchTaxCalculator.calculate` | `s7_세금_실지급액_과세사업자` / `s8_세금_비과세` |
|
||||
|
||||
---
|
||||
|
||||
## 4. 이 시나리오 작성 중 발견·보완한 사항
|
||||
|
||||
**분급 계획 합계 불일치(반올림 잔차) — 수정함.**
|
||||
- 현상: 분급액을 회차별로 `총액 × 비율 / 100` 으로 각각 반올림하면,
|
||||
원금이 7로 나눠떨어지지 않을 때 분급 합계가 원금과 1~수원 어긋났다.
|
||||
(예: 435,150원 → 회차 합 435,153원, +3원 누수)
|
||||
- 영향: 분급 총합 ≠ 원 모집수수료 → 정산 무결성(합계 일치) 위반, 과/미지급.
|
||||
- 수정: **비율 합이 정확히 100%인 정규 스케줄**일 때 마지막 회차가
|
||||
`원금 − 직전 회차 합` 으로 잔차를 흡수하도록 보정.
|
||||
(비율 합이 100% 미만인 부분지급 스케줄은 기존 동작 유지 → 설계 의도 보존)
|
||||
- 대상: `InstallmentPlanGenerator`(ga-api), `BatchInstallmentPlanGenerator`(ga-batch) 양쪽.
|
||||
|
||||
---
|
||||
|
||||
*기준: schema v108, DOMAIN_KNOWLEDGE.md 7장 공식. 모든 금액 BigDecimal HALF_UP.*
|
||||
Reference in New Issue
Block a user