fix(agent-settlement): 스냅샷 finalize 집계를 단일 누적으로 정리한다
This commit is contained in:
@@ -9,7 +9,6 @@ import kr.co.vividnext.sodalive.partner.agent.calculate.GetAgentChannelDonationS
|
|||||||
import kr.co.vividnext.sodalive.partner.agent.calculate.GetAgentChannelDonationSettlementByCreatorQueryData
|
import kr.co.vividnext.sodalive.partner.agent.calculate.GetAgentChannelDonationSettlementByCreatorQueryData
|
||||||
import kr.co.vividnext.sodalive.partner.agent.calculate.GetAgentCreatorSettlementSummaryQueryData
|
import kr.co.vividnext.sodalive.partner.agent.calculate.GetAgentCreatorSettlementSummaryQueryData
|
||||||
import kr.co.vividnext.sodalive.partner.agent.calculate.GetAgentSettlementByCreatorItem
|
import kr.co.vividnext.sodalive.partner.agent.calculate.GetAgentSettlementByCreatorItem
|
||||||
import kr.co.vividnext.sodalive.partner.agent.calculate.toMergedResponseItems
|
|
||||||
import kr.co.vividnext.sodalive.partner.agent.settlement.snapshot.AgentSettlementSnapshot
|
import kr.co.vividnext.sodalive.partner.agent.settlement.snapshot.AgentSettlementSnapshot
|
||||||
import kr.co.vividnext.sodalive.partner.agent.settlement.snapshot.AgentSettlementSnapshotRepository
|
import kr.co.vividnext.sodalive.partner.agent.settlement.snapshot.AgentSettlementSnapshotRepository
|
||||||
import kr.co.vividnext.sodalive.partner.agent.settlement.snapshot.AgentSettlementSnapshotSourceDetail
|
import kr.co.vividnext.sodalive.partner.agent.settlement.snapshot.AgentSettlementSnapshotSourceDetail
|
||||||
@@ -49,6 +48,53 @@ class AdminAgentSettlementSnapshotService(
|
|||||||
val sourceDetailsByCreatorId: Map<Long, List<SourceDetailDraft>>
|
val sourceDetailsByCreatorId: Map<Long, List<SourceDetailDraft>>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private data class SnapshotAggregateDraft(
|
||||||
|
val creatorId: Long,
|
||||||
|
val creatorNickname: String,
|
||||||
|
var assignmentId: Long? = null,
|
||||||
|
var agentSettlementRatioId: Long? = null,
|
||||||
|
var appliedAgentSettlementRatio: Int? = null,
|
||||||
|
var sourceRowCount: Int = 0,
|
||||||
|
var count: Int = 0,
|
||||||
|
var totalCan: Int = 0,
|
||||||
|
var krw: Int = 0,
|
||||||
|
var fee: Int = 0,
|
||||||
|
var settlementAmount: Int = 0,
|
||||||
|
var tax: Int = 0,
|
||||||
|
var depositAmount: Int = 0,
|
||||||
|
var agentSettlementAmount: Int = 0,
|
||||||
|
val sourceDetails: MutableList<SourceDetailDraft> = mutableListOf()
|
||||||
|
) {
|
||||||
|
fun add(
|
||||||
|
assignmentId: Long?,
|
||||||
|
agentSettlementRatioId: Long?,
|
||||||
|
appliedAgentSettlementRatio: Int?,
|
||||||
|
count: Int,
|
||||||
|
totalCan: Int,
|
||||||
|
krw: Int,
|
||||||
|
fee: Int,
|
||||||
|
settlementAmount: Int,
|
||||||
|
tax: Int,
|
||||||
|
depositAmount: Int,
|
||||||
|
agentSettlementAmount: Int,
|
||||||
|
sourceDetail: SourceDetailDraft
|
||||||
|
) {
|
||||||
|
sourceRowCount += 1
|
||||||
|
this.assignmentId = if (sourceRowCount == 1) assignmentId else null
|
||||||
|
this.agentSettlementRatioId = if (sourceRowCount == 1) agentSettlementRatioId else null
|
||||||
|
this.appliedAgentSettlementRatio = if (sourceRowCount == 1) appliedAgentSettlementRatio else null
|
||||||
|
this.count += count
|
||||||
|
this.totalCan += totalCan
|
||||||
|
this.krw += krw
|
||||||
|
this.fee += fee
|
||||||
|
this.settlementAmount += settlementAmount
|
||||||
|
this.tax += tax
|
||||||
|
this.depositAmount += depositAmount
|
||||||
|
this.agentSettlementAmount += agentSettlementAmount
|
||||||
|
sourceDetails.add(sourceDetail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun finalizeSnapshots(
|
fun finalizeSnapshots(
|
||||||
request: FinalizeAgentSettlementSnapshotRequest,
|
request: FinalizeAgentSettlementSnapshotRequest,
|
||||||
@@ -136,30 +182,57 @@ class AdminAgentSettlementSnapshotService(
|
|||||||
rows: List<GetAgentCreatorSettlementSummaryQueryData>
|
rows: List<GetAgentCreatorSettlementSummaryQueryData>
|
||||||
): SnapshotFinalizeDraft {
|
): SnapshotFinalizeDraft {
|
||||||
val (startDate, endDate) = request.toDateRange()
|
val (startDate, endDate) = request.toDateRange()
|
||||||
val rowsByCreator = rows.groupBy { it.creatorId }
|
val aggregateDrafts = linkedMapOf<Long, SnapshotAggregateDraft>()
|
||||||
val snapshots = rowsByCreator.map { (creatorId, creatorRows) ->
|
|
||||||
val merged = creatorRows.toMergedResponseItems().single()
|
|
||||||
val singleSourceRow = creatorRows.singleOrNull()
|
|
||||||
|
|
||||||
|
rows.forEach { row ->
|
||||||
|
val item = row.toResponseItem()
|
||||||
|
val sourceDetail = item.toSourceDetailDraft(
|
||||||
|
assignmentId = row.assignmentId,
|
||||||
|
agentSettlementRatioId = row.agentSettlementRatioId,
|
||||||
|
appliedAgentSettlementRatio = row.agentSettlementRatio
|
||||||
|
)
|
||||||
|
val aggregate = aggregateDrafts.getOrPut(row.creatorId) {
|
||||||
|
SnapshotAggregateDraft(
|
||||||
|
creatorId = row.creatorId,
|
||||||
|
creatorNickname = item.creatorNickname
|
||||||
|
)
|
||||||
|
}
|
||||||
|
aggregate.add(
|
||||||
|
assignmentId = row.assignmentId,
|
||||||
|
agentSettlementRatioId = row.agentSettlementRatioId,
|
||||||
|
appliedAgentSettlementRatio = row.agentSettlementRatio,
|
||||||
|
count = item.count,
|
||||||
|
totalCan = item.totalCan,
|
||||||
|
krw = item.krw,
|
||||||
|
fee = item.fee,
|
||||||
|
settlementAmount = item.settlementAmount,
|
||||||
|
tax = item.tax,
|
||||||
|
depositAmount = item.depositAmount,
|
||||||
|
agentSettlementAmount = item.agentSettlementAmount,
|
||||||
|
sourceDetail = sourceDetail
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val snapshots = aggregateDrafts.values.map { aggregate ->
|
||||||
AgentSettlementSnapshot(
|
AgentSettlementSnapshot(
|
||||||
periodStart = startDate,
|
periodStart = startDate,
|
||||||
periodEnd = endDate,
|
periodEnd = endDate,
|
||||||
settlementType = request.settlementType,
|
settlementType = request.settlementType,
|
||||||
agentId = agent.id!!,
|
agentId = agent.id!!,
|
||||||
agentNickname = agent.nickname,
|
agentNickname = agent.nickname,
|
||||||
creatorId = creatorId,
|
creatorId = aggregate.creatorId,
|
||||||
creatorNickname = merged.creatorNickname,
|
creatorNickname = aggregate.creatorNickname,
|
||||||
assignmentId = singleSourceRow?.assignmentId,
|
assignmentId = aggregate.assignmentId,
|
||||||
agentSettlementRatioId = singleSourceRow?.agentSettlementRatioId,
|
agentSettlementRatioId = aggregate.agentSettlementRatioId,
|
||||||
appliedAgentSettlementRatio = singleSourceRow?.agentSettlementRatio,
|
appliedAgentSettlementRatio = aggregate.appliedAgentSettlementRatio,
|
||||||
count = merged.count,
|
count = aggregate.count,
|
||||||
totalCan = merged.totalCan,
|
totalCan = aggregate.totalCan,
|
||||||
krw = merged.krw,
|
krw = aggregate.krw,
|
||||||
fee = merged.fee,
|
fee = aggregate.fee,
|
||||||
settlementAmount = merged.settlementAmount,
|
settlementAmount = aggregate.settlementAmount,
|
||||||
tax = merged.tax,
|
tax = aggregate.tax,
|
||||||
depositAmount = merged.depositAmount,
|
depositAmount = aggregate.depositAmount,
|
||||||
agentSettlementAmount = merged.agentSettlementAmount,
|
agentSettlementAmount = aggregate.agentSettlementAmount,
|
||||||
finalizedAt = finalizedAt,
|
finalizedAt = finalizedAt,
|
||||||
finalizedByMemberId = finalizedByMemberId
|
finalizedByMemberId = finalizedByMemberId
|
||||||
)
|
)
|
||||||
@@ -167,14 +240,8 @@ class AdminAgentSettlementSnapshotService(
|
|||||||
|
|
||||||
return SnapshotFinalizeDraft(
|
return SnapshotFinalizeDraft(
|
||||||
snapshots = snapshots,
|
snapshots = snapshots,
|
||||||
sourceDetailsByCreatorId = rowsByCreator.mapValues { (_, creatorRows) ->
|
sourceDetailsByCreatorId = aggregateDrafts.mapValues { (_, aggregate) ->
|
||||||
creatorRows.map { row ->
|
aggregate.sourceDetails.toList()
|
||||||
row.toResponseItem().toSourceDetailDraft(
|
|
||||||
assignmentId = row.assignmentId,
|
|
||||||
agentSettlementRatioId = row.agentSettlementRatioId,
|
|
||||||
appliedAgentSettlementRatio = row.agentSettlementRatio
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -187,30 +254,57 @@ class AdminAgentSettlementSnapshotService(
|
|||||||
rows: List<GetAgentChannelDonationSettlementByCreatorQueryData>
|
rows: List<GetAgentChannelDonationSettlementByCreatorQueryData>
|
||||||
): SnapshotFinalizeDraft {
|
): SnapshotFinalizeDraft {
|
||||||
val (startDate, endDate) = request.toDateRange()
|
val (startDate, endDate) = request.toDateRange()
|
||||||
val rowsByCreator = rows.groupBy { it.creatorId }
|
val aggregateDrafts = linkedMapOf<Long, SnapshotAggregateDraft>()
|
||||||
val snapshots = rowsByCreator.map { (creatorId, creatorRows) ->
|
|
||||||
val merged = creatorRows.toMergedResponseItems().single()
|
|
||||||
val singleSourceRow = creatorRows.singleOrNull()
|
|
||||||
|
|
||||||
|
rows.forEach { row ->
|
||||||
|
val item = row.toResponseItem()
|
||||||
|
val sourceDetail = item.toSourceDetailDraft(
|
||||||
|
assignmentId = row.assignmentId,
|
||||||
|
agentSettlementRatioId = row.agentSettlementRatioId,
|
||||||
|
appliedAgentSettlementRatio = row.agentSettlementRatio
|
||||||
|
)
|
||||||
|
val aggregate = aggregateDrafts.getOrPut(row.creatorId) {
|
||||||
|
SnapshotAggregateDraft(
|
||||||
|
creatorId = row.creatorId,
|
||||||
|
creatorNickname = item.creatorNickname
|
||||||
|
)
|
||||||
|
}
|
||||||
|
aggregate.add(
|
||||||
|
assignmentId = row.assignmentId,
|
||||||
|
agentSettlementRatioId = row.agentSettlementRatioId,
|
||||||
|
appliedAgentSettlementRatio = row.agentSettlementRatio,
|
||||||
|
count = item.count,
|
||||||
|
totalCan = item.totalCan,
|
||||||
|
krw = item.krw,
|
||||||
|
fee = item.fee,
|
||||||
|
settlementAmount = item.settlementAmount,
|
||||||
|
tax = item.withholdingTax,
|
||||||
|
depositAmount = item.depositAmount,
|
||||||
|
agentSettlementAmount = item.agentSettlementAmount,
|
||||||
|
sourceDetail = sourceDetail
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val snapshots = aggregateDrafts.values.map { aggregate ->
|
||||||
AgentSettlementSnapshot(
|
AgentSettlementSnapshot(
|
||||||
periodStart = startDate,
|
periodStart = startDate,
|
||||||
periodEnd = endDate,
|
periodEnd = endDate,
|
||||||
settlementType = request.settlementType,
|
settlementType = request.settlementType,
|
||||||
agentId = agent.id!!,
|
agentId = agent.id!!,
|
||||||
agentNickname = agent.nickname,
|
agentNickname = agent.nickname,
|
||||||
creatorId = creatorId,
|
creatorId = aggregate.creatorId,
|
||||||
creatorNickname = merged.creatorNickname,
|
creatorNickname = aggregate.creatorNickname,
|
||||||
assignmentId = singleSourceRow?.assignmentId,
|
assignmentId = aggregate.assignmentId,
|
||||||
agentSettlementRatioId = singleSourceRow?.agentSettlementRatioId,
|
agentSettlementRatioId = aggregate.agentSettlementRatioId,
|
||||||
appliedAgentSettlementRatio = singleSourceRow?.agentSettlementRatio,
|
appliedAgentSettlementRatio = aggregate.appliedAgentSettlementRatio,
|
||||||
count = merged.count,
|
count = aggregate.count,
|
||||||
totalCan = merged.totalCan,
|
totalCan = aggregate.totalCan,
|
||||||
krw = merged.krw,
|
krw = aggregate.krw,
|
||||||
fee = merged.fee,
|
fee = aggregate.fee,
|
||||||
settlementAmount = merged.settlementAmount,
|
settlementAmount = aggregate.settlementAmount,
|
||||||
tax = merged.withholdingTax,
|
tax = aggregate.tax,
|
||||||
depositAmount = merged.depositAmount,
|
depositAmount = aggregate.depositAmount,
|
||||||
agentSettlementAmount = merged.agentSettlementAmount,
|
agentSettlementAmount = aggregate.agentSettlementAmount,
|
||||||
finalizedAt = finalizedAt,
|
finalizedAt = finalizedAt,
|
||||||
finalizedByMemberId = finalizedByMemberId
|
finalizedByMemberId = finalizedByMemberId
|
||||||
)
|
)
|
||||||
@@ -218,14 +312,8 @@ class AdminAgentSettlementSnapshotService(
|
|||||||
|
|
||||||
return SnapshotFinalizeDraft(
|
return SnapshotFinalizeDraft(
|
||||||
snapshots = snapshots,
|
snapshots = snapshots,
|
||||||
sourceDetailsByCreatorId = rowsByCreator.mapValues { (_, creatorRows) ->
|
sourceDetailsByCreatorId = aggregateDrafts.mapValues { (_, aggregate) ->
|
||||||
creatorRows.map { row ->
|
aggregate.sourceDetails.toList()
|
||||||
row.toResponseItem().toSourceDetailDraft(
|
|
||||||
assignmentId = row.assignmentId,
|
|
||||||
agentSettlementRatioId = row.agentSettlementRatioId,
|
|
||||||
appliedAgentSettlementRatio = row.agentSettlementRatio
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package kr.co.vividnext.sodalive.admin.partner.agent.settlement
|
package kr.co.vividnext.sodalive.admin.partner.agent.settlement
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
import kr.co.vividnext.sodalive.member.Member
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
import kr.co.vividnext.sodalive.member.MemberRepository
|
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||||
import kr.co.vividnext.sodalive.member.MemberRole
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
@@ -13,6 +14,7 @@ import kr.co.vividnext.sodalive.partner.agent.settlement.snapshot.AgentSettlemen
|
|||||||
import kr.co.vividnext.sodalive.partner.agent.settlement.snapshot.FinalizeAgentSettlementSnapshotRequest
|
import kr.co.vividnext.sodalive.partner.agent.settlement.snapshot.FinalizeAgentSettlementSnapshotRequest
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||||
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.DisplayName
|
import org.junit.jupiter.api.DisplayName
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
@@ -236,4 +238,68 @@ class AdminAgentSettlementSnapshotServiceTest {
|
|||||||
Mockito.verifyNoInteractions(calculateRepository)
|
Mockito.verifyNoInteractions(calculateRepository)
|
||||||
Mockito.verifyNoInteractions(memberRepository)
|
Mockito.verifyNoInteractions(memberRepository)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("관리자 finalize는 대상 에이전트 회원이 없으면 실패한다")
|
||||||
|
fun shouldThrowWhenFinalizingSnapshotsForMissingAgent() {
|
||||||
|
val request = FinalizeAgentSettlementSnapshotRequest(
|
||||||
|
agentId = 7L,
|
||||||
|
settlementType = AgentSettlementSnapshotType.LIVE,
|
||||||
|
startDateStr = "2026-02-20",
|
||||||
|
endDateStr = "2026-02-21"
|
||||||
|
)
|
||||||
|
val (startDate, endDate) = request.toDateRange()
|
||||||
|
|
||||||
|
Mockito.`when`(
|
||||||
|
snapshotRepository.existsByPeriodStartAndPeriodEndAndSettlementTypeAndAgentId(
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
AgentSettlementSnapshotType.LIVE,
|
||||||
|
7L
|
||||||
|
)
|
||||||
|
).thenReturn(false)
|
||||||
|
Mockito.`when`(memberRepository.findById(7L)).thenReturn(Optional.empty())
|
||||||
|
|
||||||
|
val exception = assertThrows(SodaException::class.java) {
|
||||||
|
service.finalizeSnapshots(request, finalizedByMemberId = 99L)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("partner.agent.ratio.agent_not_found", exception.messageKey)
|
||||||
|
Mockito.verifyNoInteractions(calculateRepository)
|
||||||
|
Mockito.verify(snapshotRepository, Mockito.never()).saveAll(Mockito.anyList())
|
||||||
|
Mockito.verifyNoInteractions(sourceDetailRepository)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("관리자 finalize는 대상 회원이 AGENT 역할이 아니면 실패한다")
|
||||||
|
fun shouldThrowWhenFinalizingSnapshotsForNonAgentMember() {
|
||||||
|
val request = FinalizeAgentSettlementSnapshotRequest(
|
||||||
|
agentId = 7L,
|
||||||
|
settlementType = AgentSettlementSnapshotType.LIVE,
|
||||||
|
startDateStr = "2026-02-20",
|
||||||
|
endDateStr = "2026-02-21"
|
||||||
|
)
|
||||||
|
val invalidAgent = Member(password = "password", nickname = "user-a", role = MemberRole.USER)
|
||||||
|
invalidAgent.id = 7L
|
||||||
|
val (startDate, endDate) = request.toDateRange()
|
||||||
|
|
||||||
|
Mockito.`when`(
|
||||||
|
snapshotRepository.existsByPeriodStartAndPeriodEndAndSettlementTypeAndAgentId(
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
AgentSettlementSnapshotType.LIVE,
|
||||||
|
7L
|
||||||
|
)
|
||||||
|
).thenReturn(false)
|
||||||
|
Mockito.`when`(memberRepository.findById(7L)).thenReturn(Optional.of(invalidAgent))
|
||||||
|
|
||||||
|
val exception = assertThrows(SodaException::class.java) {
|
||||||
|
service.finalizeSnapshots(request, finalizedByMemberId = 99L)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("partner.agent.ratio.invalid_agent", exception.messageKey)
|
||||||
|
Mockito.verifyNoInteractions(calculateRepository)
|
||||||
|
Mockito.verify(snapshotRepository, Mockito.never()).saveAll(Mockito.anyList())
|
||||||
|
Mockito.verifyNoInteractions(sourceDetailRepository)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user