# 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>`로 교체.
