fix: 오버라이드 insurance_type_cond 미적용 과지급 수정 (PL 우선순위 진행)
- fix(HIGH): CalcOverrideStep base 가 override_rule.insurance_type_cond 를 무시하고 하위 설계사의 전 보험종류 실적 합계에 율을 곱해 과지급. 룰에 보험종류 조건이 지정되면(LIFE 등) 해당 종류 실적만 base 로 집계하도록 수정. (현 시드는 insurance_type_cond NULL 이라 잠복상태였으나 종류한정 룰 도입 시 과지급) - add: RecruitLedgerMapper/MaintainLedgerMapper.sumByAgentMonthAndType + XML - test: CalcOverrideStepTest — LIFE 한정 룰 base 필터 검증 - 전체 build+test GREEN: 153건 0실패 잔여 백로그(정책결정 필요 — 자동수정 보류): 1200%룰 초과분 clip/이연(지급정책), 계약승계 from검증(다단계 체인+contract.agent_id 갱신정책), 정착지원금 환수공식(입력 스키마), 회계 원천세 예수금 분개(회계정책), net음수 클램프(이월저장), 세금 3중경로 통일. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -80,7 +80,8 @@ public class CalcOverrideStep implements Tasklet {
|
||||
lower.getOrgId(), rule.getToGrade(), lower.getAgentId(), upperCache);
|
||||
if (upperAgentId == null) continue;
|
||||
|
||||
BigDecimal base = sumLowerCommission(lower.getAgentId(), ctx.getSettleMonth());
|
||||
BigDecimal base = sumLowerCommission(
|
||||
lower.getAgentId(), ctx.getSettleMonth(), rule.getInsuranceTypeCond());
|
||||
if (MoneyUtil.isZero(base)) continue;
|
||||
|
||||
BigDecimal amount = MoneyUtil.pct(base, rule.getOverridePct());
|
||||
@@ -105,8 +106,18 @@ public class CalcOverrideStep implements Tasklet {
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
|
||||
/** 하위 설계사의 정산월 모집 + 유지 합계 */
|
||||
private BigDecimal sumLowerCommission(Long agentId, String settleMonth) {
|
||||
/**
|
||||
* 하위 설계사의 정산월 모집 + 유지 합계.
|
||||
* 룰에 insurance_type_cond 가 지정되면 해당 보험종류 실적만 base 로 집계.
|
||||
* (지정 없음/ALL 이면 전 보험종류 합계 — 보험종류 한정 룰이 전체 실적에 곱해져 과지급되는 것을 방지)
|
||||
*/
|
||||
private BigDecimal sumLowerCommission(Long agentId, String settleMonth, String insuranceTypeCond) {
|
||||
if (insuranceTypeCond != null && !insuranceTypeCond.isBlank()
|
||||
&& !"ALL".equalsIgnoreCase(insuranceTypeCond)) {
|
||||
BigDecimal r = recruitMapper.sumByAgentMonthAndType(agentId, settleMonth, insuranceTypeCond);
|
||||
BigDecimal m = maintainMapper.sumByAgentMonthAndType(agentId, settleMonth, insuranceTypeCond);
|
||||
return MoneyUtil.add(r, m);
|
||||
}
|
||||
BigDecimal r = recruitMapper.sumByAgentMonth(agentId, settleMonth);
|
||||
BigDecimal m = maintainMapper.sumByAgentMonth(agentId, settleMonth);
|
||||
return MoneyUtil.add(r, m);
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.ga.batch.settlement.step;
|
||||
|
||||
import com.ga.batch.settlement.SettlementContext;
|
||||
import com.ga.core.mapper.ledger.MaintainLedgerMapper;
|
||||
import com.ga.core.mapper.ledger.RecruitLedgerMapper;
|
||||
import com.ga.core.mapper.org.AgentMapper;
|
||||
import com.ga.core.mapper.org.OrganizationMapper;
|
||||
import com.ga.core.mapper.rule.RuleMapper;
|
||||
import com.ga.core.mapper.settle.OverrideSettleMapper;
|
||||
import com.ga.core.vo.org.AgentResp;
|
||||
import com.ga.core.vo.org.AgentSearchParam;
|
||||
import com.ga.core.vo.rule.OverrideRuleVO;
|
||||
import com.ga.core.vo.settle.OverrideSettleVO;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 오버라이드 보험종류 조건(insurance_type_cond) 적용 검증.
|
||||
* 룰에 insurance_type_cond='LIFE' 가 있으면 base 가 LIFE 실적만으로 집계되어야 한다
|
||||
* (전 보험종류 합계에 율을 곱해 과지급되던 결함 수정).
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class CalcOverrideStepTest {
|
||||
|
||||
@Mock SettlementContext ctx;
|
||||
@Mock RuleMapper ruleMapper;
|
||||
@Mock AgentMapper agentMapper;
|
||||
@Mock OrganizationMapper organizationMapper;
|
||||
@Mock OverrideSettleMapper overrideMapper;
|
||||
@Mock RecruitLedgerMapper recruitMapper;
|
||||
@Mock MaintainLedgerMapper maintainMapper;
|
||||
|
||||
CalcOverrideStep step;
|
||||
|
||||
static final String MONTH = "202506";
|
||||
static final Long LOWER = 10L, UPPER = 99L, ORG = 100L;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
step = new CalcOverrideStep(ctx, ruleMapper, agentMapper,
|
||||
organizationMapper, overrideMapper, recruitMapper, maintainMapper);
|
||||
when(ctx.getSettleMonth()).thenReturn(MONTH);
|
||||
|
||||
// 하위 설계사(등급5) 1명 / 상위(등급1) 1명
|
||||
AgentResp lower = new AgentResp();
|
||||
lower.setAgentId(LOWER); lower.setOrgId(ORG); lower.setGradeId(5L);
|
||||
AgentResp upper = new AgentResp();
|
||||
upper.setAgentId(UPPER); upper.setOrgId(ORG); upper.setGradeId(1L);
|
||||
when(agentMapper.selectList(any(AgentSearchParam.class))).thenAnswer(inv -> {
|
||||
AgentSearchParam p = inv.getArgument(0);
|
||||
return p.getOrgId() != null ? List.of(upper) : List.of(lower); // 상위탐색 vs 전체ACTIVE
|
||||
});
|
||||
when(organizationMapper.selectAncestorIds(ORG)).thenReturn(List.of(ORG));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("insurance_type_cond='LIFE' 룰: base 는 LIFE 실적만 집계, 전체합 미사용")
|
||||
void overrideBase_filteredByInsuranceType() {
|
||||
OverrideRuleVO rule = new OverrideRuleVO();
|
||||
rule.setFromGrade(5L);
|
||||
rule.setToGrade(1L);
|
||||
rule.setOverridePct(new BigDecimal("4")); // 4%
|
||||
rule.setInsuranceTypeCond("LIFE");
|
||||
when(ruleMapper.selectOverrideRules()).thenReturn(new java.util.ArrayList<>(List.of(rule)));
|
||||
|
||||
// LIFE 실적만 1,000,000 (전체합은 호출되면 안 됨)
|
||||
when(recruitMapper.sumByAgentMonthAndType(LOWER, MONTH, "LIFE")).thenReturn(new BigDecimal("1000000"));
|
||||
when(maintainMapper.sumByAgentMonthAndType(LOWER, MONTH, "LIFE")).thenReturn(BigDecimal.ZERO);
|
||||
|
||||
ArgumentCaptor<List<OverrideSettleVO>> captor = ArgumentCaptor.forClass(List.class);
|
||||
when(overrideMapper.insertBatch(captor.capture())).thenReturn(1);
|
||||
|
||||
step.execute(null, null);
|
||||
|
||||
List<OverrideSettleVO> saved = captor.getValue();
|
||||
assertThat(saved).hasSize(1);
|
||||
assertThat(saved.get(0).getBaseAmount()).isEqualByComparingTo("1000000"); // LIFE만
|
||||
assertThat(saved.get(0).getOverrideAmount()).isEqualByComparingTo("40000.00"); // ×4%
|
||||
// 보험종류 한정 룰이면 전체합 집계는 호출되지 않아야 함
|
||||
verify(recruitMapper, never()).sumByAgentMonth(eq(LOWER), eq(MONTH));
|
||||
verify(maintainMapper, never()).sumByAgentMonth(eq(LOWER), eq(MONTH));
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,11 @@ public interface MaintainLedgerMapper {
|
||||
BigDecimal sumByAgentMonth(@Param("agentId") Long agentId,
|
||||
@Param("settleMonth") String settleMonth);
|
||||
|
||||
/** 정산월 + 설계사 + 보험종류 합계 (오버라이드 insurance_type_cond 적용용) */
|
||||
BigDecimal sumByAgentMonthAndType(@Param("agentId") Long agentId,
|
||||
@Param("settleMonth") String settleMonth,
|
||||
@Param("insuranceType") String insuranceType);
|
||||
|
||||
/** 계약별 유지 원장 수수료 합산 (1200%룰 누적 검증용) */
|
||||
BigDecimal sumByContract(@Param("contractId") Long contractId);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,11 @@ public interface RecruitLedgerMapper {
|
||||
BigDecimal sumByAgentMonth(@Param("agentId") Long agentId,
|
||||
@Param("settleMonth") String settleMonth);
|
||||
|
||||
/** 정산월 + 설계사 + 보험종류 합계 (오버라이드 insurance_type_cond 적용용) */
|
||||
BigDecimal sumByAgentMonthAndType(@Param("agentId") Long agentId,
|
||||
@Param("settleMonth") String settleMonth,
|
||||
@Param("insuranceType") String insuranceType);
|
||||
|
||||
/** 계약별 신계약 원장 수수료 합산 (1200%룰 누적 검증용) */
|
||||
BigDecimal sumByContract(@Param("contractId") Long contractId);
|
||||
|
||||
|
||||
@@ -112,6 +112,12 @@
|
||||
WHERE agent_id = #{agentId} AND settle_month = #{settleMonth}
|
||||
</select>
|
||||
|
||||
<select id="sumByAgentMonthAndType" resultType="java.math.BigDecimal">
|
||||
SELECT COALESCE(SUM(agent_amount), 0) FROM maintain_ledger
|
||||
WHERE agent_id = #{agentId} AND settle_month = #{settleMonth}
|
||||
AND insurance_type = #{insuranceType}
|
||||
</select>
|
||||
|
||||
<select id="sumByContract" resultType="java.math.BigDecimal">
|
||||
SELECT COALESCE(SUM(agent_amount), 0) FROM maintain_ledger
|
||||
WHERE contract_id = #{contractId}
|
||||
|
||||
@@ -129,6 +129,12 @@
|
||||
WHERE agent_id = #{agentId} AND settle_month = #{settleMonth}
|
||||
</select>
|
||||
|
||||
<select id="sumByAgentMonthAndType" resultType="java.math.BigDecimal">
|
||||
SELECT COALESCE(SUM(agent_amount), 0) FROM recruit_ledger
|
||||
WHERE agent_id = #{agentId} AND settle_month = #{settleMonth}
|
||||
AND insurance_type = #{insuranceType}
|
||||
</select>
|
||||
|
||||
<select id="sumByContract" resultType="java.math.BigDecimal">
|
||||
SELECT COALESCE(SUM(agent_amount), 0) FROM recruit_ledger
|
||||
WHERE contract_id = #{contractId}
|
||||
|
||||
Reference in New Issue
Block a user