Files
GA Pro bfc724669b feat: incar 갭 반영 수수료/운영 14개 도메인 풀스택 + 배치통합 + 신입교육자료
incar CMS(116메뉴) 벤치마킹 갭분석으로 ga-pro에 없던 14개 도메인을 풀스택 추가.

P7 수수료 계산 7종 (V88~V96):
- 수입수수료+수지차, 소개·이관수수료, 정착지원금, 인정실적+환산율,
  시상, 생보운영지원수수료, 지점장수당
- 각 rule/ledger + 공식(보험료×요율, 수입−지급 등) Service 내장

P8 운영/정산 7종 (V97~V105):
- 단계마감, 본사정산, 등급평가, 공동계약, 보증보험, 적립금, 수수료시뮬레이터
- close/reopen/apply/simulate/upsert 등 액션 + 손익·잔액·등급 산정 로직

배치 통합 (V106):
- settle_master.other_commission_total 컬럼 추가(DEFAULT 0, 백필)
- 설계사 지급성 9종 원장 aggregateByAgent → AggregateStep gross 합산
- 빈 원장 0 → 기존 정산결과·무결성 불변(비파괴적)

테스트: P7/P8 Service 공식 단위테스트 21건(ga-api:test 57건 전체 통과)
프론트: 화면 14개 + React.lazy 코드 스플리팅(단일 4MB → 130+ 청크)
문서: DOMAIN_GAP_P7/P8.md, 신입교육_보험과수수료_완전기초.md, DOMAIN_KNOWLEDGE/HANDOFF 갱신

검증: 전체 ./gradlew build(test 포함) SUCCESSFUL, Flyway V88~V106 success,
GET+액션POST smoke 5xx 0건, 9개 집계쿼리 SQL 유효성 확인, 공식 런타임 실측.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 15:45:10 +09:00

256 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# P7 — incar 갭 반영: 수수료 계산 7개 도메인
작성: PL (자율 모드), 2026-05-31
근거: incar CMS 116메뉴 vs ga-pro 96메뉴 갭 분석. 사용자 지시 "필요한 메뉴 적용 + 수수료 공식 다 적용".
범위: 🅰 수수료 계산 7개 도메인 풀스택 (DB→Core→API→Frontend→Infra).
기존 패턴 100% 준수 (template = 수금수수료 V41 / CollectionCommission*). 모든 금액 `NUMERIC(15,2)`, 율 `NUMERIC(7,4)`(소수, 0~1), 반올림 HALF_UP.
Flyway: **V87이 최신 → V88부터.** 각 도메인은 rule(+) ledger 2테이블 패턴. 메뉴는 GRP_COMMISSION 하위.
---
## 도메인별 설계
### D1. 수입수수료 + 수지차 (INCOME_COMM) — V88
보험사가 GA에 지급한 **수입** 측. 기존 원장(LEDGER_*)은 설계사 **지급** 측. 수지차 = 수입 지급.
**income_commission_ledger** (수입수수료 원장)
| 컬럼 | 타입 | 설명 |
|---|---|---|
| income_id | BIGSERIAL PK | |
| contract_id | BIGINT NN FK contract | |
| carrier_id | BIGINT NN FK insurance_company | 보험사 |
| settle_month | CHAR(6) NN | |
| premium_amount | NUMERIC(15,2) NN | 보험료 |
| company_rate | NUMERIC(7,4) NN | 보험사요율 |
| income_amount | NUMERIC(15,2) NN | **= premium_amount × company_rate** |
| commission_type | VARCHAR(20) NN | RECRUIT/MAINTAIN 등 |
| status | VARCHAR(20) NN DEFAULT 'CALCULATED' | CALCULATED/CONFIRMED/CANCELLED |
| created_at/updated_at | | |
idx: (carrier_id, settle_month), (contract_id), (status, settle_month)
**margin_summary** (수지차 집계 — recalc 액션으로 채움)
| 컬럼 | 타입 | 설명 |
|---|---|---|
| margin_id | BIGSERIAL PK | |
| settle_month | CHAR(6) NN | |
| agent_id | BIGINT FK agent (NULL이면 전체) | |
| income_total | NUMERIC(15,2) NN DEFAULT 0 | 수입 합 |
| payout_total | NUMERIC(15,2) NN DEFAULT 0 | 지급 합(settle_master 기준) |
| margin_amount | NUMERIC(15,2) NN DEFAULT 0 | **= income_total payout_total** |
| margin_rate | NUMERIC(7,4) | = margin/income (income>0) |
| created_at | | |
UNIQUE(settle_month, agent_id)
**공식**: income_amount = premium × company_rate. margin = Σincome Σpayout.
### D2. 소개·이관수수료 (REFERRAL_COMM) — V89
계약을 소개(REFERRAL)하거나 이관(TRANSFER)한 설계사에게 주는 수수료.
**referral_commission_rule**
| 컬럼 | 타입 | 설명 |
|---|---|---|
| rule_id | BIGSERIAL PK | |
| referral_type | VARCHAR(20) NN | REFERRAL/TRANSFER |
| calc_type | VARCHAR(10) NN | RATE/FIXED |
| commission_rate | NUMERIC(7,4) | calc_type=RATE 일 때 |
| fixed_amount | NUMERIC(15,2) | calc_type=FIXED 일 때 |
| effective_from DATE NN / effective_to DATE | | |
CHECK referral_type IN ('REFERRAL','TRANSFER'); CHECK calc_type IN ('RATE','FIXED')
**referral_commission_ledger**
| 컬럼 | 타입 | 설명 |
|---|---|---|
| ledger_id | BIGSERIAL PK | |
| contract_id | BIGINT NN FK | |
| referrer_agent_id | BIGINT NN FK agent | 소개/이관한 설계사 |
| settle_month | CHAR(6) NN | |
| referral_type | VARCHAR(20) NN | |
| base_amount | NUMERIC(15,2) NN | 기준금액(수수료/보험료) |
| commission_rate | NUMERIC(7,4) | |
| commission_amount | NUMERIC(15,2) NN | **= base×rate (RATE) 또는 fixed_amount (FIXED)** |
| status | VARCHAR(20) NN DEFAULT 'CALCULATED' | |
idx: (referrer_agent_id, settle_month),(contract_id)
### D3. 정착지원금 (SETTLING_SUPPORT) — V90
신입/경력 설계사 정착 지원금. 등급별 정액 지급 + 조기 해촉 시 환수.
**settling_support_rule**
| 컬럼 | 타입 | 설명 |
|---|---|---|
| rule_id BIGSERIAL PK | | |
| agent_grade | VARCHAR(20) NN | 설계사 등급 |
| support_amount | NUMERIC(15,2) NN | 월 정액 지원금 |
| duration_months | INT NN | 약정 지원 개월수 |
| clawback_months | INT NN | 이 개월 내 해촉 시 환수 대상 |
| effective_from DATE NN / effective_to DATE | | |
**settling_support_ledger**
| 컬럼 | 타입 | 설명 |
|---|---|---|
| ledger_id BIGSERIAL PK | | |
| agent_id BIGINT NN FK agent | | |
| settle_month CHAR(6) NN | | |
| support_amount NUMERIC(15,2) NN | 지급액 | |
| clawback_amount NUMERIC(15,2) NN DEFAULT 0 | 환수액 | |
| net_amount NUMERIC(15,2) NN | **= support_amount clawback_amount** | |
| status VARCHAR(20) NN DEFAULT 'CALCULATED' | PAID/CLAWED_BACK 포함 | |
idx: (agent_id, settle_month)
**공식**: 지급 = 등급별 support_amount. 환수 = 조기해촉시 support_amount × (잔여약정개월/약정개월) (clawback service 계산).
### D4. 인정실적 + 환산보험료 (RECOGNIZED_PERF) — V91
환산보험료 = 보험료 × 상품별환산율. 인정실적 = 환산보험료 × 인정율. 시상/등급의 기초.
**product_conversion_rate** (상품별 환산율)
| 컬럼 | 타입 | 설명 |
|---|---|---|
| rate_id BIGSERIAL PK | | |
| product_id BIGINT NN FK product | | |
| conversion_rate NUMERIC(7,4) NN | 환산율 (예: 0.5=50%) | |
| effective_from DATE NN / effective_to DATE | | |
UNIQUE(product_id, effective_from)
**recognized_performance_ledger**
| 컬럼 | 타입 | 설명 |
|---|---|---|
| ledger_id BIGSERIAL PK | | |
| agent_id BIGINT NN FK agent | | |
| contract_id BIGINT NN FK contract | | |
| settle_month CHAR(6) NN | | |
| premium_amount NUMERIC(15,2) NN | 보험료 | |
| conversion_rate NUMERIC(7,4) NN | 적용 환산율 | |
| converted_premium NUMERIC(15,2) NN | **= premium × conversion_rate** | |
| recognition_rate NUMERIC(7,4) NN | 인정율 | |
| recognized_amount NUMERIC(15,2) NN | **= converted_premium × recognition_rate** | |
| status VARCHAR(20) NN DEFAULT 'CALCULATED' | | |
idx: (agent_id, settle_month),(contract_id)
### D5. 시상 (AWARD) — V92
신인도입/법인/유지율 등 시상. 조건 충족 시 정액 또는 정률.
**award_rule**
| 컬럼 | 타입 | 설명 |
|---|---|---|
| rule_id BIGSERIAL PK | | |
| award_type VARCHAR(20) NN | NEW_RECRUIT/CORPORATE/RETENTION/PERFORMANCE | |
| calc_type VARCHAR(10) NN | RATE/FIXED | |
| threshold_amount NUMERIC(15,2) | 충족 기준 실적(이상) | |
| award_rate NUMERIC(7,4) | RATE | |
| award_fixed NUMERIC(15,2) | FIXED | |
| effective_from DATE NN / effective_to DATE | | |
CHECK award_type IN (...); CHECK calc_type IN ('RATE','FIXED')
**award_ledger**
| 컬럼 | 타입 | 설명 |
|---|---|---|
| ledger_id BIGSERIAL PK | | |
| agent_id BIGINT NN FK agent | | |
| settle_month CHAR(6) NN | | |
| award_type VARCHAR(20) NN | | |
| base_amount NUMERIC(15,2) NN | 기준 실적 | |
| award_amount NUMERIC(15,2) NN | **= base×award_rate (RATE) 또는 award_fixed (FIXED)** | |
| status VARCHAR(20) NN DEFAULT 'CALCULATED' | | |
idx: (agent_id, settle_month),(award_type, settle_month)
### D6. 생보운영지원수수료 (LIFE_OP_SUPPORT) — V93
생보 계약 운영지원 수수료. 실적 기준 지급률 + 환수.
**life_support_rule**
| 컬럼 | 타입 | 설명 |
|---|---|---|
| rule_id BIGSERIAL PK | | |
| agent_grade VARCHAR(20) NN | | |
| support_rate NUMERIC(7,4) NN | 지급률 | |
| effective_from DATE NN / effective_to DATE | | |
**life_support_ledger**
| 컬럼 | 타입 | 설명 |
|---|---|---|
| ledger_id BIGSERIAL PK | | |
| agent_id BIGINT NN FK agent | | |
| settle_month CHAR(6) NN | | |
| base_amount NUMERIC(15,2) NN | 생보 실적 | |
| support_rate NUMERIC(7,4) NN | | |
| support_amount NUMERIC(15,2) NN | **= base × support_rate** | |
| clawback_amount NUMERIC(15,2) NN DEFAULT 0 | 환수 | |
| net_amount NUMERIC(15,2) NN | = support clawback | |
| status VARCHAR(20) NN DEFAULT 'CALCULATED' | | |
idx: (agent_id, settle_month)
### D7. 지점장수당 (BRANCH_MGR_ALLOWANCE) — V94
지점장이 소속 조직 실적에 대해 받는 수당. cap 제한 + 환수.
**branch_mgr_allowance_rule**
| 컬럼 | 타입 | 설명 |
|---|---|---|
| rule_id BIGSERIAL PK | | |
| position_code VARCHAR(20) NN | 직책(지점장 등) | |
| allowance_rate NUMERIC(7,4) NN | 수당율 | |
| cap_amount NUMERIC(15,2) | 상한(NULL=무제한) | |
| effective_from DATE NN / effective_to DATE | | |
**branch_mgr_allowance_ledger**
| 컬럼 | 타입 | 설명 |
|---|---|---|
| ledger_id BIGSERIAL PK | | |
| manager_agent_id BIGINT NN FK agent | 지점장 | |
| org_id BIGINT NN FK organization | 대상 조직 | |
| settle_month CHAR(6) NN | | |
| base_amount NUMERIC(15,2) NN | 조직 실적 | |
| allowance_rate NUMERIC(7,4) NN | | |
| allowance_amount NUMERIC(15,2) NN | **= LEAST(base×rate, cap)** | |
| clawback_amount NUMERIC(15,2) NN DEFAULT 0 | | |
| status VARCHAR(20) NN DEFAULT 'CALCULATED' | | |
idx: (manager_agent_id, settle_month),(org_id, settle_month)
> **확정 FK (DB 실검증):** contract(contract_id) · agent(agent_id) · product(product_id) · organization(org_id) · **insurance_company(company_id)** · settle_master. 보험사 FK는 `carrier_id BIGINT REFERENCES insurance_company(company_id)`. 수지차 payout은 settle_master 집계로 산출(dba가 컬럼 확인).
---
## 메뉴 (V95) — GRP_COMMISSION 하위 7개 PAGE
| menu_code | menu_name | menu_path | component_path | sort |
|---|---|---|---|---|
| INCOME_COMM | 수입수수료/수지차 | /commission/income | commission/IncomeCommission | 80 |
| REFERRAL_COMM | 소개·이관수수료 | /commission/referral | commission/ReferralCommission | 90 |
| SETTLING_SUPPORT | 정착지원금 | /commission/settling-support | commission/SettlingSupport | 100 |
| RECOGNIZED_PERF | 인정실적 | /commission/recognized | commission/RecognizedPerformance | 110 |
| AWARD | 시상 | /commission/award | commission/Award | 120 |
| LIFE_OP_SUPPORT | 생보운영지원수수료 | /commission/life-support | commission/LifeSupport | 130 |
| BRANCH_MGR_ALLOWANCE | 지점장수당 | /commission/branch-allowance | commission/BranchAllowance | 140 |
권한: READ/CREATE/UPDATE/DELETE/EXPORT. 역할부여: SUPER_ADMIN/ADMIN 전체, MANAGER READ/CREATE/UPDATE/EXPORT, AGENT READ(INCOME_COMM 제외 — 본인 관련만). V54 패턴 그대로.
## 공통코드 (V96)
| group_code | 코드 |
|---|---|
| INCOME_STATUS | CALCULATED/CONFIRMED/CANCELLED |
| REFERRAL_TYPE | REFERRAL=소개/TRANSFER=이관 |
| CALC_TYPE | RATE=정률/FIXED=정액 |
| SUPPORT_STATUS | CALCULATED/PAID/CLAWED_BACK |
| AWARD_TYPE | NEW_RECRUIT=신인도입/CORPORATE=법인/RETENTION=유지율/PERFORMANCE=실적 |
CommissionType enum 확장: INCOME/REFERRAL/TRANSFER/SETTLING/RECOGNIZED/AWARD/LIFE_SUPPORT/BRANCH_ALLOWANCE 추가 + V40 COMMISSION_TYPE 공통코드 동기화.
---
## Wave 실행
| Wave | 담당 | 산출물 |
|---|---|---|
| 1 | dba-architect | V88~V96 (도메인 7 + 메뉴 + 공통코드). FK 테이블명 실검증. |
| 2 | core-architect | 14테이블 VO/Resp/SaveReq/SearchParam + Mapper IF+XML + CommissionType enum 확장 |
| 3 | api-developer | 7 Service + 7 Controller (@RequirePermission/@DataChangeLog, 공식 계산 Service 내장) |
| 4 | frontend-developer | 7 페이지 + api/commission.ts 확장 + App.tsx 라우트 |
| 5 | infra-reviewer | `./gradlew build` + 프론트 빌드 + smoke. README/HANDOFF/DOMAIN_KNOWLEDGE 갱신 |
---
## 완료 상태 (2026-05-31)
schema **v96** 적용(Flyway V88~V96 success), 전체 `./gradlew build` SUCCESSFUL, 프론트 빌드 OK, ga-api 8082 라이브. P7 14개 GET + 수지차 recalc POST 전수 호출 5xx 0건. 수입수수료(보험료×요율=6,000,000)·수지차(수입−지급) 공식 런타임 실측 검증 후 테스트데이터 정리.
**PL 통합검증에서 잡은 버그 3건(팀원 GET-only smoke가 놓침) — 수정 완료:**
1. 프론트 인정실적 경로 `/rules` → 백엔드 `/rates` 불일치 → `api/commission.ts` 수정.
2. 프론트 수지차 경로 `/margin` → 백엔드 `/margins` 불일치 → 수정.
3. 수지차 recalc 3중 결함: 컨트롤러 `@RequestBody`(프론트는 쿼리파라미터)→`@RequestParam`, 서비스 `payoutTotal=0` 하드코딩 → settle_master.net_amount 실집계(`sumPayoutByMonth`)+`sumIncomeByMonth` 신규, `selectByMonthAndAgent``#{agentId} IS NULL` Postgres typed-null 오류 → `<choose>`로 교체.