diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt b/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt index 71e1b47..c268d98 100644 --- a/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt +++ b/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt @@ -24,6 +24,7 @@ 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.transaction_result.TransactionResultRoute +import com.mob.utsmyanmar.ui.sale_void.TranDetailPage import com.mob.utsmyanmar.ui.sale_void.VoidViewModel import com.mob.utsmyanmar.ui.sale_void.VoidTraceScreen import com.mob.utsmyanmar.viewmodel.CardReaderViewModel @@ -88,10 +89,44 @@ fun AppNavGraph( VoidTraceScreen( voidViewModel = voidViewModel, + onNavigateTranDetail = { trace -> + navController.navigate(Routes.VoidTranDetail.createRoute(trace)) { + launchSingleTop = true + } + }, onBack = { navController.popBackStack() } ) } + composable( + route = Routes.VoidTranDetail.route, + arguments = listOf( + navArgument("trace") { + type = NavType.StringType + } + ) + ) { backStackEntry -> + val voidViewModel: VoidViewModel = hiltViewModel() + val sharedViewModel: SharedViewModel = hiltViewModel(activity) + val trace = backStackEntry.arguments?.getString("trace").orEmpty() + + TranDetailPage( + voidViewModel = voidViewModel, + trace = trace, + onBack = { navController.popBackStack() }, + onProceedVoid = { payDetail -> + sharedViewModel.transactionsType.value = com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.VOID + sharedViewModel.payDetail.value = payDetail + navController.navigate(Routes.SendingToHost.route) { + popUpTo(Routes.VoidTranDetail.route) { + inclusive = true + } + launchSingleTop = true + } + } + ) + } + composable(Routes.SignOn.route) { SignOnRoute( onBack = { navController.popBackStack() }, diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/navigation/Routes.kt b/app/src/main/java/com/mob/utsmyanmar/ui/navigation/Routes.kt index b26e6b3..31b1e4c 100644 --- a/app/src/main/java/com/mob/utsmyanmar/ui/navigation/Routes.kt +++ b/app/src/main/java/com/mob/utsmyanmar/ui/navigation/Routes.kt @@ -9,6 +9,9 @@ sealed class Routes(val route: String) { } data object SeeMore : Routes("see_more") 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)}" + } data object SignOn : Routes("sign_on") data object SignOnResult : Routes("sign_on_result/{isSuccess}/{message}") { fun createRoute(isSuccess: Boolean, message: String): String { diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/sale_void/TranDetailPage.kt b/app/src/main/java/com/mob/utsmyanmar/ui/sale_void/TranDetailPage.kt new file mode 100644 index 0000000..6766276 --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/ui/sale_void/TranDetailPage.kt @@ -0,0 +1,168 @@ +package com.mob.utsmyanmar.ui.sale_void + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +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.shape.RoundedCornerShape +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.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.Alignment +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.utsmyanmar.paylibs.model.PayDetail +import com.utsmyanmar.paylibs.utils.POSUtil + +@Composable +fun TranDetailPage( + voidViewModel: VoidViewModel, + trace: String, + onBack: () -> Unit, + onProceedVoid: (PayDetail) -> Unit +) { + val transaction by voidViewModel.searchTransaction(trace).observeAsState() + + Scaffold( + containerColor = Color.IvoryBeige, + topBar = { + AppBar( + title = "Transaction Detail", + icon = Icons.AutoMirrored.Filled.ArrowBack, + onIconClick = onBack + ) + } + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .padding(16.dp) + ) { + transaction?.let { payDetail -> + TransactionDetailsCard(transaction = payDetail) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = "Review the transaction before proceeding to void.", + color = Color.Gray, + fontSize = 13.sp + ) + + Spacer(modifier = Modifier.weight(1f)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Button( + onClick = onBack, + modifier = Modifier + .weight(1f) + .height(56.dp), + shape = RoundedCornerShape(8.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color.White, + contentColor = Color.LegacyRed + ) + ) { + Text("Back") + } + + Button( + onClick = { onProceedVoid(payDetail) }, + modifier = Modifier + .weight(1f) + .height(56.dp), + shape = RoundedCornerShape(8.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color.LegacyRed, + contentColor = Color.White + ) + ) { + Text("Void") + } + } + } ?: Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text( + text = "Transaction not found.", + color = Color.Gray, + fontSize = 14.sp + ) + } + } + } +} + +@Composable +private fun TransactionDetailsCard(transaction: PayDetail) { + Card( + modifier = Modifier.fillMaxWidth(), + colors = CardDefaults.cardColors(containerColor = Color.White) + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + Text( + text = "Previous Sale", + color = Color.LegacyRed, + fontWeight = FontWeight.Bold, + fontSize = 16.sp + ) + + DetailRow("Trace", transaction.voucherNo) + DetailRow("Amount", POSUtil.getInstance().formatAmount(transaction.amount)) + DetailRow("Card No", transaction.cardNo) + DetailRow("Reference", transaction.referNo) + DetailRow("Approval", transaction.approvalCode.orEmpty()) + DetailRow("Date", transaction.tradeDate) + DetailRow("Time", transaction.tradeTime) + } + } +} + +@Composable +private fun DetailRow( + label: String, + value: String +) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = label, + color = Color.Gray, + fontSize = 13.sp + ) + Text( + text = value.ifBlank { "-" }, + color = Color.Black, + fontSize = 13.sp, + fontWeight = FontWeight.Medium + ) + } +} diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/sale_void/VoidTraceScreen.kt b/app/src/main/java/com/mob/utsmyanmar/ui/sale_void/VoidTraceScreen.kt index 635e5f9..e870e51 100644 --- a/app/src/main/java/com/mob/utsmyanmar/ui/sale_void/VoidTraceScreen.kt +++ b/app/src/main/java/com/mob/utsmyanmar/ui/sale_void/VoidTraceScreen.kt @@ -41,17 +41,18 @@ import androidx.lifecycle.MutableLiveData import com.mob.utsmyanmar.ui.components.appbar.AppBar import com.mob.utsmyanmar.ui.theme.Color import com.utsmyanmar.paylibs.model.PayDetail -import com.utsmyanmar.paylibs.utils.POSUtil import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType @Composable fun VoidTraceScreen( voidViewModel: VoidViewModel, + onNavigateTranDetail: (String) -> Unit, onBack: () -> Unit = {} ) { val tag = "VoidTraceScreen" var traceNumber by remember { mutableStateOf("") } var searchedTrace by remember { mutableStateOf("") } + var lastNavigatedTrace by remember { mutableStateOf(null) } val displayTraceNumber = traceNumber.padStart(6, '0').ifEmpty { "000000" } val recentTransactions by voidViewModel.getLastThreeTransactions().observeAsState(emptyList()) @@ -78,6 +79,19 @@ fun VoidTraceScreen( } else { Color.Black } + + LaunchedEffect(searchedTrace) { + if (searchedTrace != lastNavigatedTrace) { + lastNavigatedTrace = null + } + } + + LaunchedEffect(transaction?.voucherNo) { + val matchedTrace = transaction?.voucherNo ?: return@LaunchedEffect + if (lastNavigatedTrace == matchedTrace) return@LaunchedEffect + lastNavigatedTrace = matchedTrace + onNavigateTranDetail(matchedTrace) + } LaunchedEffect(searchedTrace, transaction) { @@ -162,13 +176,7 @@ fun VoidTraceScreen( } ) - Spacer(modifier = Modifier.height(20.dp)) - - Column(modifier = Modifier.weight(1f)) { - if (transaction != null) { - TransactionDetailsCard(transaction = transaction!!) - } - } + Spacer(modifier = Modifier.weight(1f)) Row( modifier = Modifier @@ -303,55 +311,3 @@ private fun TraceKeypadButton( ) } } - -@Composable -private fun TransactionDetailsCard(transaction: PayDetail) { - Card( - modifier = Modifier.fillMaxWidth(), - colors = CardDefaults.cardColors(containerColor = Color.White) - ) { - Column( - modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(10.dp) - ) { - Text( - text = "Previous Sale", - color = Color.LegacyRed, - fontWeight = FontWeight.Bold, - fontSize = 16.sp - ) - - DetailRow("Trace", transaction.voucherNo) - DetailRow("Amount", POSUtil.getInstance().formatAmount(transaction.amount)) - DetailRow("Card No", transaction.cardNo) - DetailRow("Reference", transaction.referNo) - DetailRow("Approval", transaction.approvalCode.orEmpty()) - DetailRow("Date", transaction.tradeDate) - DetailRow("Time", transaction.tradeTime) - } - } -} - - -@Composable -private fun DetailRow( - label: String, - value: String -) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text( - text = label, - color = Color.Gray, - fontSize = 13.sp - ) - Text( - text = value.ifBlank { "-" }, - color = Color.Black, - fontSize = 13.sp, - fontWeight = FontWeight.Medium - ) - } -} diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/sending_to_host/SendingToHostRoute.kt b/app/src/main/java/com/mob/utsmyanmar/ui/sending_to_host/SendingToHostRoute.kt index b7b221e..67e0751 100644 --- a/app/src/main/java/com/mob/utsmyanmar/ui/sending_to_host/SendingToHostRoute.kt +++ b/app/src/main/java/com/mob/utsmyanmar/ui/sending_to_host/SendingToHostRoute.kt @@ -13,7 +13,7 @@ fun SendingToHostRoute( onNavigateTransactionResult: () -> Unit ) { LaunchedEffect(Unit) { - sharedViewModel.saveMockApprovedSaleForVoidTesting() + sharedViewModel.saveMockHostResultForTesting() delay(MOCK_HOST_DELAY_MS) onNavigateTransactionResult() } diff --git a/app/src/main/java/com/mob/utsmyanmar/viewmodel/SharedViewModel.kt b/app/src/main/java/com/mob/utsmyanmar/viewmodel/SharedViewModel.kt index 0dac340..57ff350 100644 --- a/app/src/main/java/com/mob/utsmyanmar/viewmodel/SharedViewModel.kt +++ b/app/src/main/java/com/mob/utsmyanmar/viewmodel/SharedViewModel.kt @@ -443,6 +443,13 @@ class SharedViewModel @Inject constructor( repository.insertPayDetail(payDetail) } + fun saveMockHostResultForTesting() { + when (transactionsType.value) { + TransactionsType.VOID -> saveMockApprovedVoidForTesting() + else -> saveMockApprovedSaleForVoidTesting() + } + } + fun saveMockApprovedSaleForVoidTesting() { val detail = payDetail.value ?: return @@ -482,6 +489,70 @@ class SharedViewModel @Inject constructor( approvalCode.value = mockApprovalCode } + fun saveMockApprovedVoidForTesting() { + val originalSale = payDetail.value ?: return + + originalSale.isCanceled = true + repository.updatePayDetail(originalSale) + + val systemParams = SystemParamsOperation.getInstance() + val mockTraceNo = systemParams.incrementSerialNum + val mockInvoiceNo = systemParams.incrementInvoiceNum + val mockApprovalCode = mockTraceNo.takeLast(6).padStart(6, '0') + val mockReferenceNo = buildString { + append(SystemDateTime.getYYMMDD()) + append(SystemDateTime.getHHmmss()) + }.takeLast(12) + + val voidDetail = PayDetail().apply { + merchantName = originalSale.merchantName + merchantNo = originalSale.merchantNo + terminalNo = originalSale.terminalNo + CardNo = originalSale.CardNo + cardType = originalSale.cardType + EXPDate = originalSale.EXPDate + cardHolderName = originalSale.cardHolderName + processCode = TransactionsType.VOID.processCode + amount = originalSale.amount + transPlatform = originalSale.transPlatform + transactionType = TransactionType.VOID + transType = TransactionsType.VOID.name + currencyCode = originalSale.currencyCode + currency = originalSale.currency + batchNo = originalSale.batchNo + voucherNo = mockTraceNo + invoiceNo = mockInvoiceNo + referNo = mockReferenceNo + approvalCode = mockApprovalCode + authNo = mockApprovalCode + accountType = originalSale.accountType + tradeAnswerCode = "00" + tradeResultDes = "MOCK VOID APPROVED" + TradeDate = SystemDateTime.getMMDD() + TradeTime = SystemDateTime.getHHmmss() + tradeDateAndTime = SystemDateTime.getMMDDhhmmss() + tradeDateTime = SystemDateTime.getYYMMDDhhmmss() + transDate = SystemDateTime.getTodayDateFormat() + transTime = SystemDateTime.getTodayTimeFormat() + originalPOSNum = originalSale.voucherNo + originalReferNo = originalSale.referNo + originalAuthNo = originalSale.approvalCode + originalBathNo = originalSale.batchNo + originalTransDate = originalSale.TradeDate + originalAmount = originalSale.amount + isCanceled = false + isSettle = false + isNeedReversal = false + isReturnGood = false + } + + repository.insertPayDetail(voidDetail) + payDetail.value = voidDetail + traceNo.value = mockTraceNo + rrNNo.value = mockReferenceNo + approvalCode.value = mockApprovalCode + } + fun enableCardStatusIcon( tapCard: Boolean, tapDevice: Boolean,