test: E2E 수수료 정산 시나리오 테스트 + 엑셀업로드/분급 버그 수정
- 시나리오 문서: 보험 인입→수수료 지급 8단계 (docs/시나리오_E2E_보험인입_수수료정산.md) - SettlementScenarioTest(9건): 계약1건 생애주기를 실제 계산기로 단계별 검증 - ExcelServiceImportTest(2건): 실 .xlsx 업로드 라운드트립 검증 - fix(CRITICAL): 엑셀 업로드 런타임 깨짐 — xlsx-streamer 2.2.0 ↔ poi 5.2.5 비호환(NoSuchMethodError). excel-streaming-reader 4.3.0(유지보수 fork)로 교체. 영향: ExcelService.importLargeExcel + UploadService(/api/upload 실엔드포인트) - fix: 분급 계획 반올림 잔차로 분급합계≠원금(정산 무결성 위반). 비율합 100% 정규스케줄일 때 마지막 회차가 잔차 흡수. ga-api/ga-batch 양쪽. - 전체 build+test GREEN: 148건 0실패 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -32,11 +32,26 @@ public class InstallmentPlanGenerator {
|
||||
String baseDate = contractDate.format(DATE_FMT);
|
||||
List<InstallmentRatioVO> ratios = ratioMapper.selectAllActiveRatios(productCategory, baseDate);
|
||||
|
||||
// 비율 합이 정확히 100%인 정규 스케줄이면 마지막 회차가 반올림 잔차를 흡수
|
||||
// → 분급 합계 == 원 수수료 보장 (부분지급 스케줄은 회차별 반올림 유지).
|
||||
boolean fullSchedule = sumRatios(ratios).compareTo(BigDecimal.valueOf(100)) == 0;
|
||||
BigDecimal allocated = BigDecimal.ZERO;
|
||||
|
||||
List<InstallmentPlanVO> plans = new ArrayList<>();
|
||||
for (InstallmentRatioVO ratio : ratios) {
|
||||
for (int i = 0; i < ratios.size(); i++) {
|
||||
InstallmentRatioVO ratio = ratios.get(i);
|
||||
// 마스터의 plan_month 가 NULL 이면 연 단위(첫 달 지급), NOT NULL 이면 그 회차(월) 그대로 사용.
|
||||
int planMonth = ratio.getPlanMonth() != null ? ratio.getPlanMonth() : 1;
|
||||
|
||||
BigDecimal amount;
|
||||
if (fullSchedule && i == ratios.size() - 1) {
|
||||
amount = totalCommission.subtract(allocated);
|
||||
} else {
|
||||
amount = totalCommission.multiply(ratio.getRatioPercent())
|
||||
.divide(BigDecimal.valueOf(100), 0, RoundingMode.HALF_UP);
|
||||
}
|
||||
allocated = allocated.add(amount);
|
||||
|
||||
InstallmentPlanVO plan = new InstallmentPlanVO();
|
||||
plan.setContractId(contractId);
|
||||
plan.setPlanYear(ratio.getPlanYear());
|
||||
@@ -44,13 +59,16 @@ public class InstallmentPlanGenerator {
|
||||
plan.setSettleMonth(
|
||||
contractDate.plusYears(ratio.getPlanYear()).plusMonths(planMonth - 1L).format(MONTH_FMT)
|
||||
);
|
||||
plan.setExpectedAmount(
|
||||
totalCommission.multiply(ratio.getRatioPercent())
|
||||
.divide(BigDecimal.valueOf(100), 0, RoundingMode.HALF_UP)
|
||||
);
|
||||
plan.setExpectedAmount(amount);
|
||||
plan.setStatus(InstallmentStatus.SCHEDULED.code());
|
||||
plans.add(plan);
|
||||
}
|
||||
return plans;
|
||||
}
|
||||
|
||||
private BigDecimal sumRatios(List<InstallmentRatioVO> ratios) {
|
||||
return ratios.stream()
|
||||
.map(InstallmentRatioVO::getRatioPercent)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import com.ga.core.mapper.upload.UploadTemplateMapper;
|
||||
import com.ga.core.vo.upload.UploadHistoryVO;
|
||||
import com.ga.core.vo.upload.UploadTemplateColumnVO;
|
||||
import com.ga.core.vo.upload.UploadTemplateVO;
|
||||
import com.monitorjbl.xlsx.StreamingReader;
|
||||
import com.github.pjfanning.xlsx.StreamingReader;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.usermodel.CellType;
|
||||
|
||||
Reference in New Issue
Block a user