feat: P11(#1) 시상/신인시상 환수 — award_ledger 환수 처리 (PL 자율진행)

incar 유지율시상환수/신인도입유치자시상환수 대응. award_ledger(V92)는 지급만 있고
환수가 없던 갭. 정착지원금/생보운영지원/지점장수당의 clawback_amount+net_amount 패턴으로
통일(신인시상=award_type NEW_RECRUIT로 기존재 → 별도 테이블 불필요).

- DB(V114): award_ledger에 clawback_amount/net_amount 컬럼 추가 + net=award 백필 +
  CHECK(clawback>=0) + status CHECK에 CLAWED_BACK 추가(pg_constraint 동적 탐색 DROP/재생성).
- core: AwardLedgerVO/Resp/SaveReq에 clawbackAmount/netAmount. AwardLedgerMapper
  aggregateByAgent SUM(award_amount)→SUM(net_amount), insert에 두 컬럼, applyClawback UPDATE.
- api: AwardService.createLedger가 net=award−clawback 계산(clawback≤award 검증). 신규
  applyClawback(ledgerId, {clawbackAmount,reason}) — 초과/취소상태 E411, status=CLAWED_BACK.
  PUT /api/awards/ledgers/{id}/clawback.
- frontend: Award 원장 그리드에 환수액/실지급 컬럼 + 합계 + 환수처리 모달(PermissionButton UPDATE).
- AggregateStep 무변경: aggregateByAgent가 net 반환 → 환수가 other_commission 자동 차감,
  net<0이면 P9 환수채권 이월에 자연 연계.

검증: build+test SUCCESSFUL, Flyway V114(schema v114). 라이브: award 100000 원장 →
clawback 30000 → net 70000·CLAWED_BACK, 초과환수 200000 → E411, aggregate net=70000 반영
확인. 테스트 데이터 정리. 스펙 docs/DOMAIN_GAP_P11.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
GA Pro
2026-06-05 00:59:48 +09:00
parent 284ecc4203
commit 934e45d295
12 changed files with 307 additions and 18 deletions
@@ -0,0 +1,56 @@
-- V114: 시상 환수 (award_ledger 컬럼 추가 + status 확장)
-- settling_support_ledger(V90) 패턴 미러:
-- clawback_amount NUMERIC(15,2) NOT NULL DEFAULT 0
-- net_amount NUMERIC(15,2) NOT NULL DEFAULT 0
-- 기존 행 백필: 환수 없던 과거 데이터는 net = award
-- status CHECK에 'CLAWED_BACK' 추가 (기존 제약은 DO 블록으로 동적 탐색 후 DROP)
-- 공통코드: AWARD_STATUS 그룹이 V92/V96/V105 어디에도 없으므로 INSERT 생략
-- ============================================================
-- 1. 컬럼 추가
-- ============================================================
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;
-- ============================================================
-- 2. 기존 행 백필: 환수 없던 과거 = net = award
-- ============================================================
UPDATE award_ledger
SET net_amount = award_amount
WHERE net_amount = 0
AND clawback_amount = 0;
-- ============================================================
-- 3. clawback 음수 방지 제약
-- ============================================================
ALTER TABLE award_ledger
ADD CONSTRAINT chk_award_clawback CHECK (clawback_amount >= 0);
-- ============================================================
-- 4. status CHECK 재생성 — CLAWED_BACK 추가
-- 기존 인라인 CHECK 이름을 추정하지 않고 pg_constraint에서 동적 조회 후 DROP
-- ============================================================
DO $$
DECLARE
c text;
BEGIN
SELECT conname INTO c
FROM pg_constraint
WHERE conrelid = 'award_ledger'::regclass
AND contype = 'c'
AND pg_get_constraintdef(oid) ILIKE '%status%';
IF c IS NOT NULL THEN
EXECUTE 'ALTER TABLE award_ledger DROP CONSTRAINT ' || quote_ident(c);
END IF;
END $$;
ALTER TABLE award_ledger
ADD CONSTRAINT chk_award_status
CHECK (status IN ('CALCULATED','CONFIRMED','PAID','CANCELLED','CLAWED_BACK'));
-- ============================================================
-- 5. 컬럼 코멘트
-- ============================================================
COMMENT ON COLUMN award_ledger.clawback_amount IS '시상 환수액';
COMMENT ON COLUMN award_ledger.net_amount IS '실지급 시상액 = award_amount clawback_amount';