feat(core): 공제 VO 세트 + Mapper + DeductionType/Status Enum (도메인 P0-2-b)

V24 공제 스키마와 ga-core 동기화. 비즈니스 로직은 P0-2-c.

신규:
- vo/settle/DeductionType {LOAN, ADVANCE, GUARANTEE, DUES, AD, CHARGEBACK_PENDING, OTHER}
- vo/settle/DeductionStatus {PENDING, APPLIED, CANCELLED}

AgentDeduction VO 세트 (표준 5종):
- AgentDeductionVO (감사 4종 포함)
- AgentDeductionResp (agentName/deductionTypeName/statusName 조인 표시 필드)
- AgentDeductionSaveReq (@Valid)
- AgentDeductionSearchParam (settleMonth/agentId/deductionType/status)

PaymentDeductionDetailVO (이체 명세 단순 VO)

Mapper (2종 신규):
- AgentDeductionMapper: selectList/Detail/insert/update/updateStatus/
  sumPendingByAgent/selectPendingByMonth
- PaymentDeductionDetailMapper: selectByPaymentId/insert/insertBatch

PaymentVO.deductionAmount + PaymentMapper.xml VOMap 갱신.
This commit is contained in:
GA Pro
2026-05-13 00:14:09 +09:00
parent 0e73eb61a9
commit a3ba7fa361
13 changed files with 386 additions and 0 deletions
@@ -0,0 +1,28 @@
package com.ga.core.mapper.settle;
import com.ga.core.vo.settle.AgentDeductionResp;
import com.ga.core.vo.settle.AgentDeductionSearchParam;
import com.ga.core.vo.settle.AgentDeductionVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
import java.util.List;
@Mapper
public interface AgentDeductionMapper {
List<AgentDeductionResp> selectList(AgentDeductionSearchParam param);
AgentDeductionResp selectDetailById(@Param("deductionId") Long deductionId);
AgentDeductionVO selectById(@Param("deductionId") Long deductionId);
int insert(AgentDeductionVO vo);
int update(AgentDeductionVO vo);
int updateStatus(@Param("deductionId") Long deductionId, @Param("status") String status);
/** 정산월 + 설계사의 PENDING 차감 합계 */
BigDecimal sumPendingByAgent(@Param("agentId") Long agentId,
@Param("settleMonth") String settleMonth);
/** 정산월 전체 PENDING 목록 (배치용) */
List<AgentDeductionVO> selectPendingByMonth(@Param("settleMonth") String settleMonth);
}
@@ -0,0 +1,14 @@
package com.ga.core.mapper.settle;
import com.ga.core.vo.settle.PaymentDeductionDetailVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface PaymentDeductionDetailMapper {
List<PaymentDeductionDetailVO> selectByPaymentId(@Param("paymentId") Long paymentId);
int insert(PaymentDeductionDetailVO vo);
int insertBatch(@Param("list") List<PaymentDeductionDetailVO> list);
}
@@ -0,0 +1,21 @@
package com.ga.core.vo.settle;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class AgentDeductionResp {
private Long deductionId;
private Long agentId;
private String agentName;
private String settleMonth;
private String deductionType;
private String deductionTypeName;
private BigDecimal amount;
private String description;
private String status;
private String statusName;
private LocalDateTime createdAt;
}
@@ -0,0 +1,30 @@
package com.ga.core.vo.settle;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class AgentDeductionSaveReq {
@NotNull(message = "설계사 ID는 필수입니다")
private Long agentId;
@NotBlank(message = "정산월은 필수입니다")
@Size(min = 6, max = 6, message = "정산월은 6자리(YYYYMM)입니다")
private String settleMonth;
@NotBlank(message = "차감유형은 필수입니다")
private String deductionType;
@NotNull(message = "차감금액은 필수입니다")
@Positive(message = "차감금액은 양수여야 합니다")
private BigDecimal amount;
@Size(max = 255)
private String description;
}
@@ -0,0 +1,14 @@
package com.ga.core.vo.settle;
import com.ga.common.model.SearchParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class AgentDeductionSearchParam extends SearchParam {
private Long agentId;
private String settleMonth;
private String deductionType;
private String status;
}
@@ -0,0 +1,21 @@
package com.ga.core.vo.settle;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class AgentDeductionVO {
private Long deductionId;
private Long agentId;
private String settleMonth;
private String deductionType; // DeductionType
private BigDecimal amount;
private String description;
private String status; // DeductionStatus
private LocalDateTime createdAt;
private Long createdBy;
private LocalDateTime updatedAt;
private Long updatedBy;
}
@@ -0,0 +1,15 @@
package com.ga.core.vo.settle;
/**
* 차감 상태 (common_code_group: DEDUCTION_STATUS, V24 기준)
* DB agent_deduction.status: PENDING / APPLIED / CANCELLED
*/
public enum DeductionStatus {
PENDING, // 대기 — 차감 등록 후 미적용
APPLIED, // 적용됨 — 이체 시 차감 완료
CANCELLED; // 취소
public String code() {
return this.name();
}
}
@@ -0,0 +1,18 @@
package com.ga.core.vo.settle;
/**
* 차감 유형 (common_code_group: DEDUCTION_TYPE, V24 기준)
*/
public enum DeductionType {
LOAN, // 대출잔액
ADVANCE, // 가불금
GUARANTEE, // 신원보증보험료
DUES, // 협회비
AD, // 광고비/판촉비
CHARGEBACK_PENDING, // 미회수환수금
OTHER; // 기타
public String code() {
return this.name();
}
}
@@ -0,0 +1,16 @@
package com.ga.core.vo.settle;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class PaymentDeductionDetailVO {
private Long detailId;
private Long paymentId;
private String deductionType; // DeductionType
private BigDecimal amount;
private String description;
private LocalDateTime appliedAt;
}
@@ -22,6 +22,7 @@ public class PaymentVO {
private BigDecimal localTaxAmount; // 지방소득세 private BigDecimal localTaxAmount; // 지방소득세
private BigDecimal vatAmount; // 부가세 private BigDecimal vatAmount; // 부가세
private BigDecimal netAmount; // 실수령액 (payAmount - 세금 합계) private BigDecimal netAmount; // 실수령액 (payAmount - 세금 합계)
private BigDecimal deductionAmount; // 공제 합계
private LocalDateTime createdAt; private LocalDateTime createdAt;
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
} }
@@ -0,0 +1,156 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ga.core.mapper.settle.AgentDeductionMapper">
<!-- ==================== ResultMap ==================== -->
<!-- agent_deduction 테이블 1:1 매핑 -->
<resultMap id="VOMap" type="com.ga.core.vo.settle.AgentDeductionVO">
<id property="deductionId" column="deduction_id"/>
<result property="agentId" column="agent_id"/>
<result property="settleMonth" column="settle_month"/>
<result property="deductionType" column="deduction_type"/>
<result property="amount" column="amount"/>
<result property="description" column="description"/>
<result property="status" column="status"/>
<result property="createdAt" column="created_at"/>
<result property="createdBy" column="created_by"/>
<result property="updatedAt" column="updated_at"/>
<result property="updatedBy" column="updated_by"/>
</resultMap>
<!-- 조인 결과 포함 API 응답용 -->
<resultMap id="RespMap" type="com.ga.core.vo.settle.AgentDeductionResp">
<id property="deductionId" column="deduction_id"/>
<result property="agentId" column="agent_id"/>
<result property="agentName" column="agent_name"/>
<result property="settleMonth" column="settle_month"/>
<result property="deductionType" column="deduction_type"/>
<result property="deductionTypeName" column="deduction_type_name"/>
<result property="amount" column="amount"/>
<result property="description" column="description"/>
<result property="status" column="status"/>
<result property="statusName" column="status_name"/>
<result property="createdAt" column="created_at"/>
</resultMap>
<!-- ==================== 공통 SELECT 컬럼 (목록/상세 공유) ==================== -->
<sql id="selectRespCols">
d.deduction_id,
d.agent_id,
a.agent_name,
d.settle_month,
d.deduction_type,
(SELECT cc.code_name FROM common_code cc
WHERE cc.group_code = 'DEDUCTION_TYPE' AND cc.code = d.deduction_type) AS deduction_type_name,
d.amount,
d.description,
d.status,
(SELECT cc.code_name FROM common_code cc
WHERE cc.group_code = 'DEDUCTION_STATUS' AND cc.code = d.status) AS status_name,
d.created_at
</sql>
<!-- ==================== SELECT ==================== -->
<!-- 목록 조회 (검색 조건 + 페이징) -->
<select id="selectList" parameterType="com.ga.core.vo.settle.AgentDeductionSearchParam"
resultMap="RespMap">
SELECT <include refid="selectRespCols"/>
FROM agent_deduction d
JOIN agent a ON a.agent_id = d.agent_id
<where>
<if test="agentId != null">
AND d.agent_id = #{agentId}
</if>
<if test="settleMonth != null and settleMonth != ''">
AND d.settle_month = #{settleMonth}
</if>
<if test="deductionType != null and deductionType != ''">
AND d.deduction_type = #{deductionType}
</if>
<if test="status != null and status != ''">
AND d.status = #{status}
</if>
</where>
ORDER BY d.deduction_id DESC
<if test="pageSize != null and pageSize > 0">
LIMIT #{pageSize} OFFSET #{offset}
</if>
</select>
<!-- 단건 조회 (Resp: 조인 포함) -->
<select id="selectDetailById" resultMap="RespMap">
SELECT <include refid="selectRespCols"/>
FROM agent_deduction d
JOIN agent a ON a.agent_id = d.agent_id
WHERE d.deduction_id = #{deductionId}
</select>
<!-- 단건 조회 (VO: CUD용) -->
<select id="selectById" resultMap="VOMap">
SELECT deduction_id, agent_id, settle_month, deduction_type,
amount, description, status,
created_at, created_by, updated_at, updated_by
FROM agent_deduction
WHERE deduction_id = #{deductionId}
</select>
<!-- 정산월 전체 PENDING 목록 (배치 처리용) -->
<select id="selectPendingByMonth" resultMap="VOMap">
SELECT deduction_id, agent_id, settle_month, deduction_type,
amount, description, status,
created_at, created_by, updated_at, updated_by
FROM agent_deduction
WHERE settle_month = #{settleMonth}
AND status = 'PENDING'
ORDER BY agent_id, deduction_id
</select>
<!-- ==================== 집계 ==================== -->
<!-- 정산월 + 설계사의 PENDING 차감 합계 (BigDecimal 반환) -->
<select id="sumPendingByAgent" resultType="java.math.BigDecimal">
SELECT COALESCE(SUM(amount), 0)
FROM agent_deduction
WHERE agent_id = #{agentId}
AND settle_month = #{settleMonth}
AND status = 'PENDING'
</select>
<!-- ==================== INSERT ==================== -->
<insert id="insert" parameterType="com.ga.core.vo.settle.AgentDeductionVO"
useGeneratedKeys="true" keyProperty="deductionId">
INSERT INTO agent_deduction (agent_id, settle_month, deduction_type,
amount, description, status,
created_at, created_by)
VALUES (#{agentId}, #{settleMonth}, #{deductionType},
#{amount}, #{description}, COALESCE(#{status}, 'PENDING'),
NOW(), #{createdBy})
</insert>
<!-- ==================== UPDATE ==================== -->
<update id="update" parameterType="com.ga.core.vo.settle.AgentDeductionVO">
UPDATE agent_deduction
<set>
<if test="deductionType != null and deductionType != ''">deduction_type = #{deductionType},</if>
<if test="amount != null">amount = #{amount},</if>
<if test="description != null">description = #{description},</if>
updated_at = NOW(),
updated_by = #{updatedBy}
</set>
WHERE deduction_id = #{deductionId}
</update>
<!-- 상태만 변경 (배치 적용/취소 시 단순 호출) -->
<update id="updateStatus">
UPDATE agent_deduction
SET status = #{status}, updated_at = NOW()
WHERE deduction_id = #{deductionId}
</update>
</mapper>
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ga.core.mapper.settle.PaymentDeductionDetailMapper">
<!-- ==================== ResultMap ==================== -->
<!-- payment_deduction_detail 테이블 1:1 매핑 -->
<resultMap id="VOMap" type="com.ga.core.vo.settle.PaymentDeductionDetailVO">
<id property="detailId" column="detail_id"/>
<result property="paymentId" column="payment_id"/>
<result property="deductionType" column="deduction_type"/>
<result property="amount" column="amount"/>
<result property="description" column="description"/>
<result property="appliedAt" column="applied_at"/>
</resultMap>
<!-- ==================== SELECT ==================== -->
<!-- 지급 ID로 공제 내역 전체 조회 (지급 상세 화면용) -->
<select id="selectByPaymentId" resultMap="VOMap">
SELECT detail_id, payment_id, deduction_type,
amount, description, applied_at
FROM payment_deduction_detail
WHERE payment_id = #{paymentId}
ORDER BY detail_id
</select>
<!-- ==================== INSERT ==================== -->
<!-- 단건 삽입 -->
<insert id="insert" parameterType="com.ga.core.vo.settle.PaymentDeductionDetailVO"
useGeneratedKeys="true" keyProperty="detailId">
INSERT INTO payment_deduction_detail (payment_id, deduction_type,
amount, description, applied_at)
VALUES (#{paymentId}, #{deductionType},
#{amount}, #{description}, COALESCE(#{appliedAt}, NOW()))
</insert>
<!-- 배치 삽입 (지급 생성 시 공제 내역 일괄 기록) -->
<insert id="insertBatch">
INSERT INTO payment_deduction_detail (payment_id, deduction_type,
amount, description, applied_at)
VALUES
<foreach collection="list" item="d" separator=",">
(#{d.paymentId}, #{d.deductionType},
#{d.amount}, #{d.description}, COALESCE(#{d.appliedAt}, NOW()))
</foreach>
</insert>
</mapper>
@@ -18,6 +18,7 @@
<result property="localTaxAmount" column="local_tax_amount"/> <result property="localTaxAmount" column="local_tax_amount"/>
<result property="vatAmount" column="vat_amount"/> <result property="vatAmount" column="vat_amount"/>
<result property="netAmount" column="net_amount"/> <result property="netAmount" column="net_amount"/>
<result property="deductionAmount" column="deduction_amount"/>
<result property="createdAt" column="created_at"/> <result property="createdAt" column="created_at"/>
<result property="updatedAt" column="updated_at"/> <result property="updatedAt" column="updated_at"/>
</resultMap> </resultMap>