Settlement Screen init

This commit is contained in:
moon 2026-05-22 00:30:51 +06:30
parent 767456b29a
commit af917520b6
7 changed files with 721 additions and 4 deletions

View File

@ -0,0 +1,11 @@
package com.mob.utsmyanmar.model
enum class TransactionStatus {
ON_SUCCESS,
ON_REVERSAL,
ON_BATCH_UPLOAD,
ON_FAIL,
ON_ERROR,
ON_SECONDARY,
ON_DONE
}

View File

@ -38,6 +38,7 @@ import androidx.compose.material3.CardDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.NavigationDrawerItem
@ -85,6 +86,7 @@ fun DashboardScreen2(
onNavigateAmount: (String) -> Unit = {},
onNavigateSignOn: () -> Unit = {},
onNavigateSeeMore: () -> Unit = {},
onNavigateSettlement: () -> Unit = {},
deviceInfoViewModel: DeviceInfoViewModel = viewModel()
) {
val deviceInfo by deviceInfoViewModel.uiState.collectAsState()
@ -92,7 +94,7 @@ fun DashboardScreen2(
LaunchedEffect(Unit) {
deviceInfoViewModel.loadDeviceInfo();
}
val drawerState = rememberDrawerState(initialValue = androidx.compose.material3.DrawerValue.Closed)
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
val scope = rememberCoroutineScope()
val mainHandler = remember { Handler(Looper.getMainLooper()) }
var showHostActionDialog by remember { mutableStateOf(false) }
@ -301,7 +303,8 @@ fun DashboardScreen2(
MenuGrid(
onNavigateAmount = onNavigateAmount,
onNavigateSignOn = onNavigateSignOn,
onNavigateSeeMore = onNavigateSeeMore
onNavigateSeeMore = onNavigateSeeMore,
onNavigateSettlement = onNavigateSettlement
)
}
}
@ -496,7 +499,8 @@ private fun IconCircle(
private fun MenuGrid(
onNavigateAmount: (String) -> Unit,
onNavigateSignOn: () -> Unit,
onNavigateSeeMore: () -> Unit
onNavigateSeeMore: () -> Unit,
onNavigateSettlement: () -> Unit
) {
Column(
verticalArrangement = Arrangement.spacedBy(10.dp),
@ -520,7 +524,12 @@ private fun MenuGrid(
Modifier.weight(1f),
onClick = onNavigateSignOn
)
MenuCard("Settlement", Icons.Default.Wallet, Modifier.weight(1f))
MenuCard(
"Settlement",
Icons.Default.Wallet,
Modifier.weight(1f),
onClick = onNavigateSettlement
)
MenuCard(
"See More",
Icons.Default.GridView,

View File

@ -23,6 +23,7 @@ import com.mob.utsmyanmar.ui.print_receipt.PrintReceiptScreen
import com.mob.utsmyanmar.ui.sign_on.SignOnResultScreen
import com.mob.utsmyanmar.ui.sign_on.SignOnRoute
import com.mob.utsmyanmar.ui.sending_to_host.SendingToHostRoute
import com.mob.utsmyanmar.ui.settlement.SettlementScreen
import com.mob.utsmyanmar.ui.transaction_result.TransactionResultRoute
import com.mob.utsmyanmar.ui.sale_void.TranDetailPage
import com.mob.utsmyanmar.ui.sale_void.VoidViewModel
@ -63,6 +64,11 @@ fun AppNavGraph(
navController.navigate(Routes.SeeMore.route) {
launchSingleTop = true
}
},
onNavigateSettlement = {
navController.navigate(Routes.Settlement.route) {
launchSingleTop = true
}
}
)
}
@ -98,6 +104,24 @@ fun AppNavGraph(
)
}
composable(Routes.Settlement.route) {
val sharedViewModel: SharedViewModel = hiltViewModel(activity)
SettlementScreen(
sharedViewModel = sharedViewModel,
onBack = { navController.popBackStack() },
onStartSettlement = {
sharedViewModel.transactionsType.value = com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT
navController.navigate(Routes.SendingToHost.route) {
popUpTo(Routes.Settlement.route) {
inclusive = true
}
launchSingleTop = true
}
}
)
}
composable(
route = Routes.VoidTranDetail.route,
arguments = listOf(

View File

@ -8,6 +8,7 @@ sealed class Routes(val route: String) {
fun createRoute(action: String): String = "amount/${Uri.encode(action)}"
}
data object SeeMore : Routes("see_more")
data object Settlement : Routes("settlement")
data object VoidTrace : Routes("void_trace")
data object VoidTranDetail : Routes("void_tran_detail/{trace}") {
fun createRoute(trace: String): String = "void_tran_detail/${Uri.encode(trace)}"

View File

@ -0,0 +1,194 @@
package com.mob.utsmyanmar.ui.settlement
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.mob.utsmyanmar.ui.components.appbar.AppBar
import com.mob.utsmyanmar.ui.theme.Color
import com.mob.utsmyanmar.viewmodel.SharedViewModel
import com.utsmyanmar.paylibs.model.PayDetail
import com.utsmyanmar.paylibs.utils.POSUtil
@Composable
fun SettlementScreen(
sharedViewModel: SharedViewModel,
onBack: () -> Unit,
onStartSettlement: () -> Unit
) {
val records by sharedViewModel.getSettlementRecords().observeAsState(emptyList())
val totalAmount = records.sumOf { it.amount }
Scaffold(
containerColor = Color.IvoryBeige,
topBar = {
AppBar(
title = "Settlement",
icon = Icons.AutoMirrored.Filled.ArrowBack,
onIconClick = onBack
)
}
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.padding(16.dp)
) {
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = Color.White)
) {
Column(modifier = Modifier.padding(18.dp)) {
Text(
text = "Settlement Summary",
color = Color.LegacyRed,
fontSize = 18.sp,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(12.dp))
SummaryRow("Record Count", records.size.toString())
SummaryRow("Total Amount", POSUtil.getInstance().formatAmount(totalAmount))
}
}
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Unsettled Records",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.SemiBold
)
Spacer(modifier = Modifier.height(8.dp))
Card(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
colors = CardDefaults.cardColors(containerColor = Color.White)
) {
if (records.isEmpty()) {
Text(
text = "No records available for settlement.",
color = Color.Gray,
fontSize = 14.sp,
modifier = Modifier.padding(18.dp)
)
} else {
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(0.dp)
) {
items(records) { record ->
SettlementRecordRow(record = record)
}
}
}
}
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = onStartSettlement,
enabled = records.isNotEmpty(),
modifier = Modifier
.fillMaxWidth()
.height(56.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Color.LegacyRed,
contentColor = Color.White
)
) {
Text("Start Settlement")
}
}
}
}
@Composable
private fun SummaryRow(
label: String,
value: String
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = label,
color = Color.Gray,
fontSize = 13.sp
)
Text(
text = value,
color = Color.Black,
fontSize = 13.sp,
fontWeight = FontWeight.Medium
)
}
}
@Composable
private fun SettlementRecordRow(record: PayDetail) {
Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = record.transType.ifBlank { "UNKNOWN" },
color = Color.LegacyRed,
fontSize = 14.sp,
fontWeight = FontWeight.Bold
)
Text(
text = POSUtil.getInstance().formatAmount(record.amount),
color = Color.Black,
fontSize = 14.sp,
fontWeight = FontWeight.SemiBold
)
}
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "Trace ${record.voucherNo.ifBlank { "-" }}",
color = Color.Black,
fontSize = 12.sp
)
Text(
text = "Card ${record.cardNo.ifBlank { "-" }}",
color = Color.Gray,
fontSize = 12.sp
)
Spacer(modifier = Modifier.height(12.dp))
HorizontalDivider(color = Color.Gray.copy(alpha = 0.25f))
}
}

View File

@ -0,0 +1,440 @@
package com.mob.utsmyanmar.ui.settlement
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mob.utsmyanmar.config.Constants
import com.mob.utsmyanmar.model.SettlementType
import com.mob.utsmyanmar.model.TransactionStatus
import com.utsmyanmar.baselib.repo.Repository
import com.utsmyanmar.paylibs.Constant
import com.utsmyanmar.paylibs.batch_upload.BatchListener
import com.utsmyanmar.paylibs.batch_upload.BatchUploadProcess
import com.utsmyanmar.paylibs.isobuilder.ISOMode
import com.utsmyanmar.paylibs.isobuilder.builderx.ISOMsgX
import com.utsmyanmar.paylibs.isobuilder.builderx.ISOVersion
import com.utsmyanmar.paylibs.model.MsgField
import com.utsmyanmar.paylibs.model.PayDetail
import com.utsmyanmar.paylibs.model.SettleData
import com.utsmyanmar.paylibs.model.TradeData
import com.utsmyanmar.paylibs.network.ISOCallback
import com.utsmyanmar.paylibs.network.ISOSocket
import com.utsmyanmar.paylibs.utils.MessageType
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation
import com.utsmyanmar.paylibs.utils.enums.HostName
import com.utsmyanmar.paylibs.utils.iso_utils.BitmapConfig
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionType
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType
import com.utsmyanmar.paylibs.utils.params.Params
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import sunmi.sunmiui.utils.LogUtil
import java.util.Locale
import javax.inject.Inject
data class SettlementUiState(
val saleCount: Int = 0,
val saleAmount: Long = 0L,
val preCount: Int = 0,
val preAmount: Long = 0L,
val refundCount: Int = 0,
val refundAmount: Long = 0L,
val caCount: Int = 0,
val caAmount: Long = 0L,
val settlementType: SettlementType? = null,
val isNoData: Boolean = false,
val isSentData: Boolean = false,
val bottomLayout: Int = 0,
val status: TransactionStatus? = null,
val isLoading: Boolean = false
)
sealed interface SettlementEvent {
data class ShowStatus(val status: TransactionStatus) : SettlementEvent
}
@HiltViewModel
class SettlementViewModel @Inject constructor(
private val repository: Repository
) : ViewModel() {
companion object {
private val TAG = SettlementViewModel::class.java.simpleName
}
private var payDetail: PayDetail? = null
private var payDetails: List<PayDetail>? = null
private val deleteTrans = arrayListOf<PayDetail>()
private var flag = false
private var errorFlag = false
private var isSecondCall = false
private var batchIndex = 0
private var bitmap = ""
private val isoMsgX: ISOMsgX =
ISOMsgX.ISOMsgXBuilder(
ISOVersion.VERSION_1993,
ISOMode.BOTH_HEADER_TPDU,
HostName.BPC
).build()
private val _uiState = MutableStateFlow(SettlementUiState())
val uiState = _uiState.asStateFlow()
private val _events = Channel<SettlementEvent>(Channel.BUFFERED)
val events = _events.receiveAsFlow()
fun getLastSettlement(voucherNo: String): LiveData<List<PayDetail>> {
return repository.getLastSettlement(voucherNo)
}
fun getSettlement(): LiveData<List<PayDetail>> {
return repository.getSettlement()
}
fun getSettlementPOS(): LiveData<List<PayDetail>> {
return repository.getSettlementPOS()
}
fun getDeleteTrans(batchNo: String): LiveData<List<PayDetail>> {
return repository.getDeleteTrans(batchNo)
}
fun getAdditionalSettlementPOS(): LiveData<List<PayDetail>> {
return repository.getAdditionalSettlementPOS(
SystemParamsOperation.getInstance().getCurrentBatchNum()
)
}
fun setPayDetails(list: List<PayDetail>) {
payDetails = list
}
fun setSettlementSummary(
saleCount: Int,
saleAmount: Long,
preCount: Int,
preAmount: Long,
refundCount: Int,
refundAmount: Long,
caCount: Int,
caAmount: Long
) {
_uiState.update {
it.copy(
saleCount = saleCount,
saleAmount = saleAmount,
preCount = preCount,
preAmount = preAmount,
refundCount = refundCount,
refundAmount = refundAmount,
caCount = caCount,
caAmount = caAmount
)
}
}
fun setSettlementType(type: SettlementType) {
_uiState.update {
it.copy(settlementType = type)
}
}
fun updatePayDetail(payDetail: PayDetail) {
repository.updatePayDetail(payDetail)
}
private fun insertPayDetail(payDetail: PayDetail) {
repository.insertPayDetail(payDetail)
}
private fun updateDB() {
payDetails?.forEach { pay ->
repository.deletePayDetail(pay)
}
deleteTrans.forEach { pay ->
repository.deletePayDetail(pay)
}
payDetails = emptyList()
deleteTrans.clear()
}
fun startSettlementProcess() {
_uiState.update {
it.copy(isLoading = true)
}
SystemParamsOperation.getInstance().getIncrementBatchNo()
requestOnlineProcessSettlement()
}
fun startPrintSettlementProcess() {
// PrintReceipt.getInstance().printSettlementReceiptPOS(...)
}
fun testServiceClass() {
LogUtil.d(TAG, "SettlementViewModel works!")
}
private fun requestOnlineProcessSettlement() {
val state = _uiState.value
val hostName = HostName.BPC
val field60 = SystemParamsOperation.getInstance().getCurrentBatchNum()
val sale2Count = state.saleCount + state.preCount
val sale2Amount = state.saleAmount + state.preAmount
val totalSaleCount = String.format(Locale.getDefault(), "%03d", sale2Count)
val totalSaleAmount = String.format(Locale.getDefault(), "%010d00", sale2Amount)
val totalRefundCount = String.format(Locale.getDefault(), "%03d", state.refundCount)
val totalRefundAmount = String.format(
Locale.getDefault(),
"%010d00",
if (state.refundAmount == 0L) 0 else state.refundAmount
)
val totalDebitSaleCount = String.format(Locale.getDefault(), "%03d", state.caCount)
val totalDebitSaleAmount = String.format(
Locale.getDefault(),
"%010d00",
if (state.caAmount == 0L) 0 else state.caAmount
)
val totalERefundCount = String.format(Locale.getDefault(), "%03d", 0)
val totalERefundAmount = String.format(Locale.getDefault(), "%010d00", 0)
val tradeData = Params.newTrade(true)
val currentPayDetail = tradeData.payDetail
payDetail = currentPayDetail
bitmap = BitmapConfig.BPC_SETTLEMENT
currentPayDetail.transType = TransactionsType.SETTLEMENT.name
currentPayDetail.transactionType = TransactionType.SETTLEMENT
if (!flag) {
currentPayDetail.processCode = TransactionsType.SETTLEMENT.processCode
} else {
bitmap = BitmapConfig.BPC_SETTLEMENT_TRAILER
currentPayDetail.processCode = "910000"
}
currentPayDetail.batchNo = SystemParamsOperation.getInstance().getCurrentBatchNum()
currentPayDetail.settleList =
"${state.saleCount}:${state.saleAmount}-" +
"${state.caCount}:${state.caAmount}-" +
"${state.refundCount}:${state.refundAmount}-" +
"${state.preCount}:${state.preAmount}"
val settleData = SettleData(
state.saleCount,
state.saleAmount,
state.preCount,
state.preAmount,
state.refundCount,
state.refundAmount,
state.caCount,
state.caAmount
)
currentPayDetail.settleDataObj = settleData
if (hostName == HostName.BPC) {
val totalAmount =
state.saleAmount + state.preAmount + state.refundAmount + state.caAmount
val settlementData = if (state.refundAmount != 0L) {
val creditTotal = state.saleAmount + state.preAmount + state.caAmount
val subTotal = creditTotal - state.refundAmount
if (subTotal < 0L) {
"D" + String.format(Locale.getDefault(), "%012d", kotlin.math.abs(subTotal))
} else {
"C" + String.format(Locale.getDefault(), "%012d", subTotal)
}
} else {
"C" + String.format(Locale.getDefault(), "%012d", totalAmount)
}
currentPayDetail.settleData = settlementData
currentPayDetail.amount = totalAmount
} else {
currentPayDetail.settleData =
totalSaleCount +
totalSaleAmount +
totalRefundCount +
totalRefundAmount +
totalDebitSaleCount +
totalDebitSaleAmount +
totalERefundCount +
totalERefundAmount
}
tradeData.payDetail = currentPayDetail
tradeData.field60 = field60
val sendBytes = isoMsgX.buildISOPackets(
tradeData,
bitmap,
MessageType.SETTLEMENT
)
LogUtil.d(TAG, "Starting SETTLEMENT process...")
ISOSocket.getInstance().enqueue(
sendBytes,
sendBytes.size,
false,
object : ISOCallback {
override fun onReceive(bytes: ByteArray, length: Int) {
val responseMap: Map<String, MsgField>? =
isoMsgX.parseISOPackets(bytes, length)
if (responseMap != null) {
val resultStr = try {
responseMap["F039"]?.dataStr.orEmpty()
} catch (e: NullPointerException) {
e.printStackTrace()
currentPayDetail.isNeedReversal = true
return
}
currentPayDetail.tradeAnswerCode = resultStr
when {
resultStr == Constant.ANSWER_CODE_ACCEPT ||
resultStr == Constant.ANSWER_CODE_APPROVED -> {
currentPayDetail.isNeedReversal = false
}
resultStr == "95" || resultStr == "095" -> {
currentPayDetail.isNeedReversal = !flag
}
}
} else {
errorFlag = true
}
}
override fun onError(msg: String) {
if (msg != Constants.REVERSAL) {
if (!isSecondCall) {
ISOSocket.getInstance().switchIp()
postStatus(TransactionStatus.ON_SECONDARY)
isSecondCall = true
requestOnlineProcessSettlement()
} else {
postStatus(TransactionStatus.ON_ERROR)
currentPayDetail.isNeedReversal = true
isSecondCall = false
}
} else {
postStatus(TransactionStatus.ON_ERROR)
}
}
override fun onComplete() {
if (currentPayDetail.isNeedReversal) {
flag = true
batchUploadProcess()
} else {
flag = false
batchIndex = 0
updateDB()
insertPayDetail(currentPayDetail)
if (errorFlag) {
postStatus(TransactionStatus.ON_ERROR)
} else {
postStatus(TransactionStatus.ON_SUCCESS)
}
_uiState.update {
it.copy(isLoading = false)
}
}
}
}
)
}
private fun batchUploadProcess() {
postStatus(TransactionStatus.ON_BATCH_UPLOAD)
val currentPayDetails = payDetails
if (currentPayDetails.isNullOrEmpty()) {
requestOnlineProcessSettlement()
return
}
val uploadPayDetail = currentPayDetails[batchIndex]
val tradeData = TradeData().apply {
payDetail = uploadPayDetail
}
BatchUploadProcess.getInstance()
.enqueue(tradeData)
.startBatchUpload(object : BatchListener {
override fun onSuccessBatch() {
if (batchIndex < currentPayDetails.size - 1) {
LogUtil.d(TAG, "Pay detail size: ${currentPayDetails.size}")
LogUtil.d(TAG, "Count value: $batchIndex")
batchIndex++
batchUploadProcess()
} else {
requestOnlineProcessSettlement()
}
LogUtil.e(TAG, "Batch Upload Success")
}
override fun onFailBatch() {
LogUtil.e(TAG, "Batch Upload Fail")
postStatus(TransactionStatus.ON_ERROR)
_uiState.update {
it.copy(isLoading = false)
}
}
})
}
private fun postStatus(status: TransactionStatus) {
_uiState.update {
it.copy(status = status)
}
viewModelScope.launch {
_events.send(SettlementEvent.ShowStatus(status))
}
}
}

View File

@ -431,6 +431,10 @@ class SharedViewModel @Inject constructor(
return repository.getLastThreeTransactions()
}
fun getSettlementRecords(): LiveData<List<PayDetail>> {
return repository.getSettlementPOS()
}
fun getReversalTransaction(voucherNo: String): LiveData<PayDetail> {
return repository.getReversalTransaction(voucherNo)
}
@ -445,6 +449,7 @@ class SharedViewModel @Inject constructor(
fun saveMockHostResultForTesting() {
when (transactionsType.value) {
TransactionsType.SETTLEMENT -> saveMockSettlementForTesting()
TransactionsType.VOID -> saveMockApprovedVoidForTesting()
else -> saveMockApprovedSaleForVoidTesting()
}
@ -553,6 +558,39 @@ class SharedViewModel @Inject constructor(
approvalCode.value = mockApprovalCode
}
fun saveMockSettlementForTesting() {
val systemParams = SystemParamsOperation.getInstance()
val mockTraceNo = systemParams.incrementSerialNum
val mockInvoiceNo = systemParams.incrementInvoiceNum
val settlementDetail = PayDetail().apply {
merchantNo = systemParams.merchantId
merchantName = systemParams.merchantName
terminalNo = systemParams.terminalId
voucherNo = mockTraceNo
invoiceNo = mockInvoiceNo
batchNo = systemParams.systemParamsSettings.batchNumStart
processCode = TransactionsType.SETTLEMENT.processCode
transactionType = TransactionType.SETTLEMENT
transType = TransactionsType.SETTLEMENT.name
currencyCode = systemParams.currencyType.removed0CurrencyCode
tradeAnswerCode = "00"
tradeResultDes = "MOCK SETTLEMENT APPROVED"
TradeDate = SystemDateTime.getMMDD()
TradeTime = SystemDateTime.getHHmmss()
tradeDateAndTime = SystemDateTime.getMMDDhhmmss()
tradeDateTime = SystemDateTime.getYYMMDDhhmmss()
transDate = SystemDateTime.getTodayDateFormat()
transTime = SystemDateTime.getTodayTimeFormat()
isSettle = false
isNeedReversal = false
isCanceled = false
}
payDetail.value = settlementDetail
traceNo.value = mockTraceNo
}
fun enableCardStatusIcon(
tapCard: Boolean,
tapDevice: Boolean,