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:
@@ -31,5 +31,14 @@ public interface AwardLedgerMapper {
|
||||
|
||||
int updateStatus(@Param("ledgerId") Long ledgerId, @Param("status") String status);
|
||||
|
||||
/**
|
||||
* 환수 처리 — clawback_amount/net_amount/status/updated_at 갱신.
|
||||
* status 는 통상 'CLAWED_BACK'. Service에서 clawback ≤ award_amount 검증 후 호출.
|
||||
*/
|
||||
int applyClawback(@Param("ledgerId") Long ledgerId,
|
||||
@Param("clawbackAmount") BigDecimal clawbackAmount,
|
||||
@Param("netAmount") BigDecimal netAmount,
|
||||
@Param("status") String status);
|
||||
|
||||
int delete(@Param("ledgerId") Long ledgerId);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,10 @@ public class AwardLedgerResp {
|
||||
private String awardTypeName;
|
||||
private BigDecimal baseAmount;
|
||||
private BigDecimal awardAmount;
|
||||
/** 환수액 (V114) */
|
||||
private BigDecimal clawbackAmount;
|
||||
/** 실지급액 = award_amount - clawback_amount (V114) */
|
||||
private BigDecimal netAmount;
|
||||
private String status;
|
||||
/** 공통코드 INCOME_STATUS */
|
||||
private String statusName;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.ga.core.vo.commission;
|
||||
|
||||
import jakarta.validation.constraints.DecimalMin;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
@@ -10,6 +11,7 @@ import java.math.BigDecimal;
|
||||
/**
|
||||
* award_ledger 등록 요청.
|
||||
* award_amount 는 Service가 계산 후 VO에 set.
|
||||
* net_amount 는 Service가 award_amount - clawbackAmount 계산 후 VO에 set.
|
||||
*/
|
||||
@Data
|
||||
public class AwardLedgerSaveReq {
|
||||
@@ -26,4 +28,8 @@ public class AwardLedgerSaveReq {
|
||||
@NotNull
|
||||
@Positive
|
||||
private BigDecimal baseAmount;
|
||||
|
||||
/** 등록 시 환수액 (선택, 기본 0). clawback ≤ award_amount 검증은 Service에서 수행. */
|
||||
@DecimalMin("0")
|
||||
private BigDecimal clawbackAmount;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@ import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* award_ledger — 시상 원장 (V92)
|
||||
* award_ledger — 시상 원장 (V92, clawback 컬럼 V114 추가)
|
||||
* award_amount = base_amount × award_rate (RATE) or award_fixed (FIXED)
|
||||
* net_amount = award_amount - clawback_amount
|
||||
*/
|
||||
@Data
|
||||
public class AwardLedgerVO {
|
||||
@@ -21,7 +22,11 @@ public class AwardLedgerVO {
|
||||
private BigDecimal baseAmount;
|
||||
/** 시상금 */
|
||||
private BigDecimal awardAmount;
|
||||
/** CALCULATED / CONFIRMED / PAID / CANCELLED */
|
||||
/** 환수액 (V114) */
|
||||
private BigDecimal clawbackAmount;
|
||||
/** 실지급액 = award_amount - clawback_amount (V114) */
|
||||
private BigDecimal netAmount;
|
||||
/** CALCULATED / CONFIRMED / PAID / CANCELLED / CLAWED_BACK */
|
||||
private String status;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<resultMap id="VOMap" type="com.ga.core.vo.commission.AwardLedgerVO"/>
|
||||
<resultMap id="RespMap" type="com.ga.core.vo.commission.AwardLedgerResp"/>
|
||||
|
||||
<!-- 목록/단건 조회 컬럼 — 설계사명·조직명·공통코드명 조인 포함 -->
|
||||
<!-- 목록/단건 조회 컬럼 — 설계사명·조직명·공통코드명 조인 포함 (V114: clawback_amount, net_amount 추가) -->
|
||||
<sql id="ledgerJoinCols">
|
||||
l.ledger_id,
|
||||
l.agent_id, a.agent_name, o.org_name,
|
||||
@@ -15,6 +15,7 @@
|
||||
(SELECT cc.code_name FROM common_code cc
|
||||
WHERE cc.group_code = 'AWARD_TYPE' AND cc.code = l.award_type) AS award_type_name,
|
||||
l.base_amount, l.award_amount,
|
||||
l.clawback_amount, l.net_amount,
|
||||
l.status,
|
||||
(SELECT cc.code_name FROM common_code cc
|
||||
WHERE cc.group_code = 'INCOME_STATUS' AND cc.code = l.status) AS status_name
|
||||
@@ -54,12 +55,14 @@
|
||||
AND status != 'CANCELLED'
|
||||
</select>
|
||||
|
||||
<!-- award_amount 는 Service가 계산 후 VO에 set, status 기본 CALCULATED -->
|
||||
<!-- award_amount/net_amount 는 Service가 계산 후 VO에 set, status 기본 CALCULATED (V114: clawback_amount, net_amount 포함) -->
|
||||
<insert id="insert" useGeneratedKeys="true" keyProperty="ledgerId">
|
||||
INSERT INTO award_ledger
|
||||
(agent_id, settle_month, award_type, base_amount, award_amount, status)
|
||||
(agent_id, settle_month, award_type, base_amount, award_amount,
|
||||
clawback_amount, net_amount, status)
|
||||
VALUES
|
||||
(#{agentId}, #{settleMonth}, #{awardType}, #{baseAmount}, #{awardAmount},
|
||||
COALESCE(#{clawbackAmount}, 0), #{netAmount},
|
||||
COALESCE(#{status}, 'CALCULATED'))
|
||||
</insert>
|
||||
|
||||
@@ -73,14 +76,24 @@
|
||||
DELETE FROM award_ledger WHERE ledger_id = #{ledgerId}
|
||||
</delete>
|
||||
|
||||
<!-- 정산월 기준 설계사별 시상금 합계 -->
|
||||
<!-- 정산월 기준 설계사별 시상금 합계 — net_amount 기준(V114: 환수 자동 차감). CANCELLED 제외. -->
|
||||
<select id="aggregateByAgent" resultType="map">
|
||||
SELECT agent_id AS "agentId",
|
||||
SUM(award_amount) AS "total"
|
||||
SELECT agent_id AS "agentId",
|
||||
SUM(net_amount) AS "total"
|
||||
FROM award_ledger
|
||||
WHERE settle_month = #{settleMonth}
|
||||
AND status != 'CANCELLED'
|
||||
GROUP BY agent_id
|
||||
</select>
|
||||
|
||||
<!-- 환수 처리 — clawback_amount/net_amount/status/updated_at 갱신 -->
|
||||
<update id="applyClawback">
|
||||
UPDATE award_ledger
|
||||
SET clawback_amount = #{clawbackAmount},
|
||||
net_amount = #{netAmount},
|
||||
status = #{status},
|
||||
updated_at = NOW()
|
||||
WHERE ledger_id = #{ledgerId}
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
|
||||
Reference in New Issue
Block a user