에이전트 기능 #416
@@ -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.GetAgentCreatorSettlementSummaryQueryData
|
||||
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.AgentSettlementSnapshotRepository
|
||||
import kr.co.vividnext.sodalive.partner.agent.settlement.snapshot.AgentSettlementSnapshotSourceDetail
|
||||
@@ -49,6 +48,53 @@ class AdminAgentSettlementSnapshotService(
|
||||
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
|
||||
fun finalizeSnapshots(
|
||||
request: FinalizeAgentSettlementSnapshotRequest,
|
||||
@@ -136,30 +182,57 @@ class AdminAgentSettlementSnapshotService(
|
||||
rows: List<GetAgentCreatorSettlementSummaryQueryData>
|
||||
): SnapshotFinalizeDraft {
|
||||
val (startDate, endDate) = request.toDateRange()
|
||||
val rowsByCreator = rows.groupBy { it.creatorId }
|
||||
val snapshots = rowsByCreator.map { (creatorId, creatorRows) ->
|
||||
val merged = creatorRows.toMergedResponseItems().single()
|
||||
val singleSourceRow = creatorRows.singleOrNull()
|
||||
val aggregateDrafts = linkedMapOf<Long, SnapshotAggregateDraft>()
|
||||
|
||||
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(
|
||||
periodStart = startDate,
|
||||
periodEnd = endDate,
|
||||
settlementType = request.settlementType,
|
||||
agentId = agent.id!!,
|
||||
agentNickname = agent.nickname,
|
||||
creatorId = creatorId,
|
||||
creatorNickname = merged.creatorNickname,
|
||||
assignmentId = singleSourceRow?.assignmentId,
|
||||
agentSettlementRatioId = singleSourceRow?.agentSettlementRatioId,
|
||||
appliedAgentSettlementRatio = singleSourceRow?.agentSettlementRatio,
|
||||
count = merged.count,
|
||||
totalCan = merged.totalCan,
|
||||
krw = merged.krw,
|
||||
fee = merged.fee,
|
||||
settlementAmount = merged.settlementAmount,
|
||||
tax = merged.tax,
|
||||
depositAmount = merged.depositAmount,
|
||||
agentSettlementAmount = merged.agentSettlementAmount,
|
||||
creatorId = aggregate.creatorId,
|
||||
creatorNickname = aggregate.creatorNickname,
|
||||
assignmentId = aggregate.assignmentId,
|
||||
agentSettlementRatioId = aggregate.agentSettlementRatioId,
|
||||
appliedAgentSettlementRatio = aggregate.appliedAgentSettlementRatio,
|
||||
count = aggregate.count,
|
||||
totalCan = aggregate.totalCan,
|
||||
krw = aggregate.krw,
|
||||
fee = aggregate.fee,
|
||||
settlementAmount = aggregate.settlementAmount,
|
||||
tax = aggregate.tax,
|
||||
depositAmount = aggregate.depositAmount,
|
||||
agentSettlementAmount = aggregate.agentSettlementAmount,
|
||||
finalizedAt = finalizedAt,
|
||||
finalizedByMemberId = finalizedByMemberId
|
||||
)
|
||||
@@ -167,14 +240,8 @@ class AdminAgentSettlementSnapshotService(
|
||||
|
||||
return SnapshotFinalizeDraft(
|
||||
snapshots = snapshots,
|
||||
sourceDetailsByCreatorId = rowsByCreator.mapValues { (_, creatorRows) ->
|
||||
creatorRows.map { row ->
|
||||
row.toResponseItem().toSourceDetailDraft(
|
||||
assignmentId = row.assignmentId,
|
||||
agentSettlementRatioId = row.agentSettlementRatioId,
|
||||
appliedAgentSettlementRatio = row.agentSettlementRatio
|
||||
)
|
||||
}
|
||||
sourceDetailsByCreatorId = aggregateDrafts.mapValues { (_, aggregate) ->
|
||||
aggregate.sourceDetails.toList()
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -187,30 +254,57 @@ class AdminAgentSettlementSnapshotService(
|
||||
rows: List<GetAgentChannelDonationSettlementByCreatorQueryData>
|
||||
): SnapshotFinalizeDraft {
|
||||
val (startDate, endDate) = request.toDateRange()
|
||||
val rowsByCreator = rows.groupBy { it.creatorId }
|
||||
val snapshots = rowsByCreator.map { (creatorId, creatorRows) ->
|
||||
val merged = creatorRows.toMergedResponseItems().single()
|
||||
val singleSourceRow = creatorRows.singleOrNull()
|
||||
val aggregateDrafts = linkedMapOf<Long, SnapshotAggregateDraft>()
|
||||
|
||||
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(
|
||||
periodStart = startDate,
|
||||
periodEnd = endDate,
|
||||
settlementType = request.settlementType,
|
||||
agentId = agent.id!!,
|
||||
agentNickname = agent.nickname,
|
||||
creatorId = creatorId,
|
||||
creatorNickname = merged.creatorNickname,
|
||||
assignmentId = singleSourceRow?.assignmentId,
|
||||
agentSettlementRatioId = singleSourceRow?.agentSettlementRatioId,
|
||||
appliedAgentSettlementRatio = singleSourceRow?.agentSettlementRatio,
|
||||
count = merged.count,
|
||||
totalCan = merged.totalCan,
|
||||
krw = merged.krw,
|
||||
fee = merged.fee,
|
||||
settlementAmount = merged.settlementAmount,
|
||||
tax = merged.withholdingTax,
|
||||
depositAmount = merged.depositAmount,
|
||||
agentSettlementAmount = merged.agentSettlementAmount,
|
||||
creatorId = aggregate.creatorId,
|
||||
creatorNickname = aggregate.creatorNickname,
|
||||
assignmentId = aggregate.assignmentId,
|
||||
agentSettlementRatioId = aggregate.agentSettlementRatioId,
|
||||
appliedAgentSettlementRatio = aggregate.appliedAgentSettlementRatio,
|
||||
count = aggregate.count,
|
||||
totalCan = aggregate.totalCan,
|
||||
krw = aggregate.krw,
|
||||
fee = aggregate.fee,
|
||||
settlementAmount = aggregate.settlementAmount,
|
||||
tax = aggregate.tax,
|
||||
depositAmount = aggregate.depositAmount,
|
||||
agentSettlementAmount = aggregate.agentSettlementAmount,
|
||||
finalizedAt = finalizedAt,
|
||||
finalizedByMemberId = finalizedByMemberId
|
||||
)
|
||||
@@ -218,14 +312,8 @@ class AdminAgentSettlementSnapshotService(
|
||||
|
||||
return SnapshotFinalizeDraft(
|
||||
snapshots = snapshots,
|
||||
sourceDetailsByCreatorId = rowsByCreator.mapValues { (_, creatorRows) ->
|
||||
creatorRows.map { row ->
|
||||
row.toResponseItem().toSourceDetailDraft(
|
||||
assignmentId = row.assignmentId,
|
||||
agentSettlementRatioId = row.agentSettlementRatioId,
|
||||
appliedAgentSettlementRatio = row.agentSettlementRatio
|
||||
)
|
||||
}
|
||||
sourceDetailsByCreatorId = aggregateDrafts.mapValues { (_, aggregate) ->
|
||||
aggregate.sourceDetails.toList()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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.MemberRepository
|
||||
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 org.junit.jupiter.api.Assertions.assertEquals
|
||||
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.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -236,4 +238,68 @@ class AdminAgentSettlementSnapshotServiceTest {
|
||||
Mockito.verifyNoInteractions(calculateRepository)
|
||||
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