MPU to MPU/JCB/UPI

This commit is contained in:
moon 2026-09-21 16:37:42 +06:30
parent d59933f00a
commit a2e2829649
4 changed files with 181 additions and 22 deletions

View File

@ -15,16 +15,19 @@ 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.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.mob.utsmyanmar.ui.theme.Color
import com.mob.utsmyanmar.utils.convertDateString
import com.utsmyanmar.paylibs.model.PayDetail
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation
@Composable
internal fun HistoryTransactionCard(
@ -71,43 +74,126 @@ internal fun HistoryTransactionCard(
}
@Composable
internal fun HistoryTransactionDetailCard(payDetail: PayDetail) {
internal fun HistoryTransactionDetailCard(
payDetail: PayDetail,
modifier: Modifier = Modifier,
) {
val dateStr = if (payDetail.transDate.isNotBlank()) {
convertDateString(payDetail.transDate)
} else {
payDetail.tradeDate.ifBlank { "-" }
}
val timeStr = payDetail.transTime.ifBlank { payDetail.tradeTime }.ifBlank { "-" }
val merchantNameStr = payDetail.merchantName.takeIf { it.isNotBlank() }
?: SystemParamsOperation.getInstance().merchantName?.takeIf { it.isNotBlank() }
?: "-"
val midStr = payDetail.merchantNo.takeIf { it.isNotBlank() }
?: SystemParamsOperation.getInstance().merchantId?.takeIf { it.isNotBlank() }
?: "-"
val tidStr = payDetail.terminalNo.takeIf { it.isNotBlank() }
?: SystemParamsOperation.getInstance().terminalId?.takeIf { it.isNotBlank() }
?: "-"
val traceNoStr = payDetail.voucherNo.ifBlank { "-" }
val refNoStr = payDetail.referNo.ifBlank { "-" }
val aprCodeStr = payDetail.approvalCode?.takeIf { it.isNotBlank() }
?: payDetail.authNo.takeIf { it.isNotBlank() }
?: "-"
val transTypeStr = displayTransactionTitle(payDetail)
val amountStr = displayAmountWithSign(payDetail)
val cardTypeStr = payDetail.accountType.ifBlank { "-" }
val cleanPan = payDetail.cardNo.orEmpty().replace(" ", "")
val cardNoStr = if (cleanPan.length > 10) {
cleanPan.take(6) + "*".repeat(cleanPan.length - 10) + cleanPan.takeLast(4)
} else if (cleanPan.isNotBlank()) {
cleanPan
} else {
"-"
}
val statusStr = displayTransactionStatus(payDetail)
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = Color.White)
modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = displayTransactionTitle(payDetail),
text = transTypeStr,
color = Color.LegacyRed,
style = MaterialTheme.typography.titleMedium
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold
)
Text(
text = amountStr,
color = Color.Black,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold
)
}
HorizontalDivider(
modifier = Modifier.padding(vertical = 2.dp),
color = Color.Gray.copy(alpha = 0.2f)
)
SummaryLine("Date", dateStr)
SummaryLine("Time", timeStr)
SummaryLine("Merchant Name", merchantNameStr)
SummaryLine("Mid", midStr)
SummaryLine("Tid", tidStr)
SummaryLine("Trace No", traceNoStr)
SummaryLine("Ref No", refNoStr)
SummaryLine("APR Code", aprCodeStr)
SummaryLine("Transaction Type", transTypeStr)
SummaryLine("Amount", amountStr)
SummaryLine("Card Type", cardTypeStr)
SummaryLine("Card No", cardNoStr)
SummaryLine(
label = "Status",
value = statusStr,
valueColor = when {
statusStr.contains("SUCCESS", ignoreCase = true) ||
statusStr.contains("Approved", ignoreCase = true) -> Color.Success
statusStr.contains("FAIL", ignoreCase = true) ||
statusStr.contains("DECLINE", ignoreCase = true) -> Color.Error
else -> Color.Black
}
)
SummaryLine("Date", payDetail.tradeDate)
SummaryLine("Time", payDetail.tradeTime)
SummaryLine("Amount", displayAmountWithSign(payDetail))
SummaryLine("Trace No", payDetail.voucherNo)
// SummaryLine("Card No", payDetail.cardNo)
SummaryLine("Reference No", payDetail.referNo)
SummaryLine("Approval Code", payDetail.approvalCode.orEmpty())
SummaryLine("Status", displayTransactionStatus(payDetail))
}
}
}
@Composable
internal fun SummaryLine(label: String, value: String) {
internal fun SummaryLine(
label: String,
value: String,
valueColor: androidx.compose.ui.graphics.Color = Color.Black,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(label, color = Color.Gray, style = MaterialTheme.typography.bodyMedium)
Text(
value.ifBlank { "-" },
text = label,
color = Color.Gray,
style = MaterialTheme.typography.bodyMedium
)
Text(
text = value.ifBlank { "-" },
color = valueColor,
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Medium
)
}
}

View File

@ -159,7 +159,8 @@ fun HistoryRoute(
is HistoryDestination.ReviewDetail -> ReviewDetailScreen(
viewModel = viewModel,
trace = current.trace,
onBack = { destination = HistoryDestination.ReviewTrace }
onBack = { destination = HistoryDestination.ReviewTrace },
onReprint = ::reprintTransaction
)
HistoryDestination.BatchTotal -> BatchTotalScreen(

View File

@ -2,10 +2,19 @@ package com.mob.utsmyanmar.ui.history_functions
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
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.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
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.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -15,16 +24,34 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.mob.utsmyanmar.ui.components.appbar.AppBar
import com.mob.utsmyanmar.ui.preview.P2Preview
import com.mob.utsmyanmar.ui.preview.P3Preview
import com.mob.utsmyanmar.ui.theme.Color
import com.mob.utsmyanmar.ui.theme.MOBPOSTheme
import com.utsmyanmar.paylibs.model.PayDetail
@Composable
fun ReviewDetailScreen(
viewModel: HistoryViewModel,
trace: String,
onBack: () -> Unit,
onReprint: ((PayDetail) -> Unit)? = null,
) {
val transaction by viewModel.searchTransaction(trace).observeAsState()
ReviewDetailContent(
transaction = transaction,
onBack = onBack,
onReprint = onReprint
)
}
@Composable
fun ReviewDetailContent(
transaction: PayDetail?,
onBack: () -> Unit,
onReprint: ((PayDetail) -> Unit)? = null,
) {
Scaffold(
containerColor = Color.IvoryBeige,
topBar = {
@ -40,9 +67,28 @@ fun ReviewDetailScreen(
.fillMaxSize()
.padding(paddingValues)
.padding(16.dp)
.verticalScroll(rememberScrollState())
) {
transaction?.let { payDetail ->
HistoryTransactionDetailCard(payDetail)
if (onReprint != null && isValidForReprint(payDetail)) {
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = { onReprint(prepareReprintPayDetail(payDetail)) },
modifier = Modifier
.fillMaxWidth()
.height(48.dp),
shape = RoundedCornerShape(8.dp),
colors = ButtonDefaults.buttonColors(containerColor = Color.LegacyRed)
) {
Text(
text = "Reprint",
style = MaterialTheme.typography.titleMedium,
color = Color.White
)
}
}
} ?: Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
@ -52,3 +98,29 @@ fun ReviewDetailScreen(
}
}
}
@P2Preview
@P3Preview
@Composable
private fun ReviewDetailScreenPreview() {
MOBPOSTheme {
ReviewDetailContent(
transaction = PayDetail().apply {
transDate = "21/09/26"
transTime = "14:04:15"
merchantName = "UTS Merchant"
merchantNo = "100089476209176"
terminalNo = "10420257"
voucherNo = "000001"
referNo = "123456789012"
approvalCode = "123456"
transType = "SALE"
amount = 1500000L
accountType = "MPU"
cardNo = "9503036867315548"
tradeAnswerCode = "00"
},
onBack = {}
)
}
}

View File

@ -52,7 +52,7 @@ fun SelectHostScreen(
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
HostButton("MPU") { onSelectHost(HostType.MPU) }
HostButton("MPU / JCB / UPI") { onSelectHost(HostType.MPU) }
HostButton("Visa / Master") {
if (SystemParamsOperation.getInstance().isEmvEnabled) {
onSelectHost(HostType.VISA_MASTER)