63 lines
4.3 KiB
Markdown
63 lines
4.3 KiB
Markdown
|
|
# P11 — 시상/신인시상 환수 (#1)
|
|||
|
|
|
|||
|
|
작성: PL, 2026-06-04. 후속 갭 3종 중 두 번째.
|
|||
|
|
근거: incar `유지율시상환수`/`신인도입유치자시상환수`/`시상내역 조회`. GA award_ledger(V92)는
|
|||
|
|
지급만 있고 환수(clawback) 처리가 없음. 정착지원금/생보운영지원/지점장수당은 이미
|
|||
|
|
`clawback_amount`+`net_amount` 패턴 보유 → **award_ledger도 동일 패턴으로 통일**.
|
|||
|
|
|
|||
|
|
신인시상은 award_type='NEW_RECRUIT'로 이미 존재 → 별도 테이블 불필요. 모든 award_type에 환수 적용.
|
|||
|
|
Flyway: **V113 최신 → V114**. 금액 NUMERIC(15,2), HALF_UP.
|
|||
|
|
|
|||
|
|
## 1. 현재 award_ledger (V92)
|
|||
|
|
ledger_id / agent_id / settle_month / award_type(NEW_RECRUIT/CORPORATE/RETENTION/PERFORMANCE) /
|
|||
|
|
base_amount / award_amount / status(CALCULATED/CONFIRMED/PAID/CANCELLED) / created_at / updated_at.
|
|||
|
|
aggregateByAgent = SUM(award_amount) WHERE status!=CANCELLED.
|
|||
|
|
|
|||
|
|
**문제**: clawback_amount/net_amount 없음. settling/life/branch는 net=support−clawback, aggregate=SUM(net).
|
|||
|
|
|
|||
|
|
## 2. 스키마 변경 (V114) — settling_support_ledger 패턴 미러
|
|||
|
|
```sql
|
|||
|
|
ALTER TABLE award_ledger
|
|||
|
|
ADD COLUMN clawback_amount NUMERIC(15,2) NOT NULL DEFAULT 0,
|
|||
|
|
ADD COLUMN net_amount NUMERIC(15,2) NOT NULL DEFAULT 0;
|
|||
|
|
-- 기존 행 백필: 환수 없던 과거 = net = award
|
|||
|
|
UPDATE award_ledger SET net_amount = award_amount WHERE net_amount = 0 AND clawback_amount = 0;
|
|||
|
|
-- clawback 음수 방지 + 상태에 CLAWED_BACK 추가(settling 패턴과 통일)
|
|||
|
|
ALTER TABLE award_ledger ADD CONSTRAINT chk_award_clawback CHECK (clawback_amount >= 0);
|
|||
|
|
ALTER TABLE award_ledger DROP CONSTRAINT <기존 status CHECK 이름>; -- \d 로 확인 (예: award_ledger_status_check)
|
|||
|
|
ALTER TABLE award_ledger ADD CONSTRAINT chk_award_status
|
|||
|
|
CHECK (status IN ('CALCULATED','CONFIRMED','PAID','CANCELLED','CLAWED_BACK'));
|
|||
|
|
```
|
|||
|
|
> 기존 status CHECK 제약명은 `\d award_ledger` 또는 information_schema 로 정확히 확인 후 DROP. 추정명 사용 금지.
|
|||
|
|
> net_amount는 음수 허용(환수>시상 시 음수 가능)이나, **§4 검증에서 clawback ≤ award_amount 강제**하므로 실제 net≥0. (지급성이므로 음수 차단 — settling 동일)
|
|||
|
|
|
|||
|
|
메뉴 변경 없음(award 화면 기존). 공통코드 AWARD_STATUS에 CLAWED_BACK 있으면 추가(없으면 생략).
|
|||
|
|
|
|||
|
|
## 3. Core
|
|||
|
|
- AwardLedgerVO/Resp/SaveReq에 `clawbackAmount`, `netAmount` 추가.
|
|||
|
|
- `AwardLedgerMapper.aggregateByAgent` XML: `SUM(award_amount)` → **`SUM(net_amount)`** (net=award−clawback). status!=CANCELLED 유지.
|
|||
|
|
- 신규 Mapper 메서드 `applyClawback(@Param ledgerId, @Param clawbackAmount, @Param netAmount, @Param status)` (+XML): clawback_amount/net_amount/status='CLAWED_BACK'/updated_at 갱신.
|
|||
|
|
- createLedger 시 net_amount = award_amount − clawback_amount 저장(insert XML에 두 컬럼 포함).
|
|||
|
|
|
|||
|
|
## 4. API (AwardService 확장)
|
|||
|
|
- `createLedger`: clawback_amount(SaveReq, 기본0) 받아 **clawback ≤ award_amount 검증(초과 시 E411)**, net = award − clawback 계산해 저장.
|
|||
|
|
- **신규 액션** `applyClawback(ledgerId, AwardClawbackReq{clawbackAmount, reason})` @Transactional:
|
|||
|
|
- 원장 조회(없으면 NOT_FOUND). 이미 CANCELLED면 E411.
|
|||
|
|
- clawbackAmount > award_amount 면 E411("환수액이 시상액 초과").
|
|||
|
|
- net = award_amount − clawbackAmount. mapper.applyClawback(ledgerId, clawback, net, 'CLAWED_BACK').
|
|||
|
|
- @DataChangeLog.
|
|||
|
|
- Controller: `PUT /api/awards/ledgers/{id}/clawback` (@RequirePermission(menu=AWARD, action=UPDATE)). 기존 award 컨트롤러 경로 패턴 확인.
|
|||
|
|
|
|||
|
|
> **AggregateStep 무변경**: awardMapper.aggregateByAgent가 SUM(net_amount) 반환하므로 환수가 자동으로 other_commission 차감에 반영. net<0이면 P9 환수채권 이월에 자연 연계.
|
|||
|
|
|
|||
|
|
## 5. Frontend (기존 Award 화면 확장)
|
|||
|
|
- award 원장 그리드에 환수액/실지급(net) 컬럼 추가.
|
|||
|
|
- 행 액션 "환수처리"(PermissionButton UPDATE) → 모달(환수액 입력 + 사유) → PUT /clawback. 성공 시 invalidate.
|
|||
|
|
- api 모듈에 applyClawback 추가. 기존 award api/페이지 파일명 확인 후 확장.
|
|||
|
|
|
|||
|
|
## 6. 검증(PL)
|
|||
|
|
build+test, Flyway V114(award_ledger 컬럼/제약/백필), 라이브: award 원장 1건 생성(net=award) →
|
|||
|
|
applyClawback(부분환수) → net 감소·status CLAWED_BACK 확인 → clawback>award E411 → aggregateByAgent
|
|||
|
|
가 net 반영하는지 SQL 확인. 테스트 데이터 정리.
|
|||
|
|
</content>
|