Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-19 16:07:08 +05:00
parent a0e5656330
commit d82dbdd1e6
12 changed files with 256 additions and 129 deletions

View file

@ -66,8 +66,8 @@ object OrderConflictRules {
}
private fun OrderType.isIssuing(): Boolean {
return this == OrderType.CARD_ISSUE ||
this == OrderType.CARD_ISSUE_ADDITIONAL ||
return this == OrderType.CARD_ISSUE_ADDITIONAL ||
this == OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC ||
this == OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC_V2
}

View file

@ -9,9 +9,9 @@ import com.tangem.domain.pay.model.OrderType.Companion.fromString
* so the app never crashes on a new server-side type.
*/
enum class OrderType(val wireValue: String) {
CARD_ISSUE("CARD_ISSUE_VIRTUAL_RAIN_KYC"),
CARD_ISSUE_ADDITIONAL("CARD_ISSUE_ADDITIONAL"),
CARD_ISSUE_VIRTUAL_RAIN_KYC_V2("CARD_ISSUE_VIRTUAL_RAIN_KYC_V2"),
CARD_ISSUE_VIRTUAL_RAIN_KYC("CARD_ISSUE_VIRTUAL_RAIN_KYC"),
CARD_REISSUE("CARD_REISSUE"),
CARD_FREEZE("CARD_FREEZE"),
CARD_UNFREEZE("CARD_UNFREEZE"),

View file

@ -6,6 +6,7 @@ import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.model.Offer
import com.tangem.domain.pay.model.OrderType
import com.tangem.domain.pay.model.TangemPayOrderInfo
import com.tangem.domain.pay.repository.CustomerOffersRepository
import com.tangem.domain.pay.repository.CustomerOrderRepository
@ -53,7 +54,14 @@ class IssueAdditionalCardUseCase(
val activeOrders = catch(
block = {
customerOrderRepository
.findOrders(userWalletId = userWalletId, types = setOf(offer.data.orderType))
.findOrders(
userWalletId = userWalletId,
types = setOf(
offer.data.orderType,
OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC,
OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC_V2,
),
)
.bind()
},
catch = { handleError(it) },

View file

@ -10,7 +10,7 @@ internal class OrderConflictRulesTest {
@Test
fun `IssueCard is blocked by an active issue order`() {
val active = listOf(order(type = OrderType.CARD_ISSUE, status = OrderStatus.PROCESSING))
val active = listOf(order(type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC, status = OrderStatus.PROCESSING))
val resolution = OrderConflictRules.resolve(OrderIntent.IssueCard, active)
@ -104,7 +104,7 @@ internal class OrderConflictRulesTest {
val active = listOf(
order(type = OrderType.CARD_FREEZE, status = OrderStatus.PROCESSING, productInstanceId = cardA),
order(type = OrderType.WITHDRAW, status = OrderStatus.PROCESSING),
order(type = OrderType.CARD_ISSUE, status = OrderStatus.PROCESSING),
order(type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC, status = OrderStatus.PROCESSING),
)
val resolution = OrderConflictRules.resolve(OrderIntent.Rename(cardA), active)
@ -115,8 +115,8 @@ internal class OrderConflictRulesTest {
@Test
fun `Terminal-status orders never block`() {
val terminal = listOf(
order(type = OrderType.CARD_ISSUE, status = OrderStatus.COMPLETED),
order(type = OrderType.CARD_ISSUE, status = OrderStatus.CANCELED),
order(type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC, status = OrderStatus.COMPLETED),
order(type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC, status = OrderStatus.CANCELED),
)
val resolution = OrderConflictRules.resolve(OrderIntent.IssueCard, terminal)

View file

@ -65,7 +65,11 @@ internal class IssueAdditionalCardUseCaseTest {
coEvery {
orderRepository.findOrders(
userWalletId,
types = setOf(OrderType.CARD_ISSUE_ADDITIONAL),
types = setOf(
OrderType.CARD_ISSUE_ADDITIONAL,
OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC,
OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC_V2,
),
statuses = emptySet(),
)
} returns listOf(existing).right()
@ -83,7 +87,11 @@ internal class IssueAdditionalCardUseCaseTest {
coEvery {
orderRepository.findOrders(
userWalletId,
types = setOf(OrderType.CARD_ISSUE_ADDITIONAL),
types = setOf(
OrderType.CARD_ISSUE_ADDITIONAL,
OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC,
OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC_V2,
),
statuses = emptySet(),
)
} returns emptyList<Order>().right()
@ -107,7 +115,11 @@ internal class IssueAdditionalCardUseCaseTest {
coEvery {
orderRepository.findOrders(
userWalletId,
types = setOf(OrderType.CARD_ISSUE_ADDITIONAL),
types = setOf(
OrderType.CARD_ISSUE_ADDITIONAL,
OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC,
OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC_V2,
),
statuses = emptySet(),
)
} returns emptyList<Order>().right()

View file

@ -35,7 +35,7 @@ internal class RestoreActiveOrdersUseCaseTest {
@Test
fun `returns the orders found by the repository`() = runTest {
val orders = listOf(
order(id = "issue", type = OrderType.CARD_ISSUE, status = OrderStatus.PROCESSING),
order(id = "issue", type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC, status = OrderStatus.PROCESSING),
order(id = "withdraw", type = OrderType.WITHDRAW, status = OrderStatus.NEW),
)
coEvery { repository.findOrders(userWalletId, types = emptySet(), statuses = any()) } returns orders.right()

View file

@ -12,11 +12,11 @@ internal class OrderResolverTest {
fun `selectActive filters by type and active status`() {
val orders = listOf(
order(id = "1", type = OrderType.WITHDRAW, status = OrderStatus.PROCESSING, updatedAt = "2026-01-01"),
order(id = "2", type = OrderType.CARD_ISSUE, status = OrderStatus.PROCESSING, updatedAt = "2026-01-02"),
order(id = "3", type = OrderType.CARD_ISSUE, status = OrderStatus.COMPLETED, updatedAt = "2026-01-03"),
order(id = "2", type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC, status = OrderStatus.PROCESSING, updatedAt = "2026-01-02"),
order(id = "3", type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC, status = OrderStatus.COMPLETED, updatedAt = "2026-01-03"),
)
val result = OrderResolver.selectActive(orders = orders, type = OrderType.CARD_ISSUE)
val result = OrderResolver.selectActive(orders = orders, type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC)
assertThat(result?.id).isEqualTo("2")
}
@ -24,11 +24,11 @@ internal class OrderResolverTest {
@Test
fun `selectActive picks the latest by updatedAt`() {
val orders = listOf(
order(id = "old", type = OrderType.CARD_ISSUE, status = OrderStatus.NEW, updatedAt = "2026-01-01"),
order(id = "new", type = OrderType.CARD_ISSUE, status = OrderStatus.PROCESSING, updatedAt = "2026-06-05"),
order(id = "old", type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC, status = OrderStatus.NEW, updatedAt = "2026-01-01"),
order(id = "new", type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC, status = OrderStatus.PROCESSING, updatedAt = "2026-06-05"),
)
val result = OrderResolver.selectActive(orders = orders, type = OrderType.CARD_ISSUE)
val result = OrderResolver.selectActive(orders = orders, type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC)
assertThat(result?.id).isEqualTo("new")
}
@ -36,11 +36,11 @@ internal class OrderResolverTest {
@Test
fun `selectActive returns null when no active order of the type exists`() {
val orders = listOf(
order(id = "1", type = OrderType.CARD_ISSUE, status = OrderStatus.COMPLETED, updatedAt = "2026-01-01"),
order(id = "1", type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC, status = OrderStatus.COMPLETED, updatedAt = "2026-01-01"),
order(id = "2", type = OrderType.WITHDRAW, status = OrderStatus.PROCESSING, updatedAt = "2026-01-02"),
)
val result = OrderResolver.selectActive(orders = orders, type = OrderType.CARD_ISSUE)
val result = OrderResolver.selectActive(orders = orders, type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC)
assertThat(result).isNull()
}
@ -91,11 +91,11 @@ internal class OrderResolverTest {
@Test
fun `selectLatest includes terminal orders`() {
val orders = listOf(
order(id = "1", type = OrderType.CARD_ISSUE, status = OrderStatus.COMPLETED, updatedAt = "2026-01-05"),
order(id = "2", type = OrderType.CARD_ISSUE, status = OrderStatus.NEW, updatedAt = "2026-01-01"),
order(id = "1", type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC, status = OrderStatus.COMPLETED, updatedAt = "2026-01-05"),
order(id = "2", type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC, status = OrderStatus.NEW, updatedAt = "2026-01-01"),
)
val result = OrderResolver.selectLatest(orders = orders, type = OrderType.CARD_ISSUE)
val result = OrderResolver.selectLatest(orders = orders, type = OrderType.CARD_ISSUE_VIRTUAL_RAIN_KYC)
assertThat(result?.id).isEqualTo("1")
}