Compare commits

..

No commits in common. "2323562270f92700e95df3dd152d1d3531ad3c53" and "8223b4877ff9c6a26bc3a5f7fd83fb58f19c22b1" have entirely different histories.

21 changed files with 117 additions and 363 deletions

View File

@ -40,8 +40,8 @@ android {
dimension = "env"
applicationIdSuffix = ".uat"
versionNameSuffix = "-UAT"
versionCode = 17
versionName = "3.5"
versionCode = 15
versionName = "3.3"
buildConfigField("String", "ENV", "\"UAT\"")
resValue("string", "app_name", "MOB-UAT")
}

View File

@ -38,6 +38,3 @@ Mark intentional simplifications with a ponytail: comment. If the shortcut has a
Not lazy about: understanding the problem (read it fully and trace the real flow before picking a rung, a small diff you don't understand is just laziness dressed up as efficiency), input validation at trust boundaries, error handling that prevents data loss, security, accessibility, the calibration real hardware needs (the platform is never the spec ideal, a clock drifts, a sensor reads off), anything explicitly requested. Lazy code without its check is unfinished: non-trivial logic leaves ONE runnable check behind, the smallest thing that fails if the logic breaks (an assert-based demo/self-check or one small test file; no frameworks, no fixtures). Trivial one-liners need no test.
(Yes, this file also applies to agents working on the ponytail repo itself. Especially to them.)
### Don't Do This
- don't run ./grdlew

View File

@ -29,7 +29,7 @@ object SettlementScheduler {
val configuredTime = parseTime(
// SystemParamsOperation.getInstance().clearBatchTime
"21:35:00"
"10:50:00"
) ?: parseTime(FALLBACK_ALERT_TIME)!!
val calendar = Calendar.getInstance().apply {

View File

@ -389,10 +389,6 @@ private fun CardLogoRow() {
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically
) {
CardLogo(
icon = painterResource(R.drawable.logo_mpu),
name = "master",
)
CardLogo(
icon = painterResource(R.drawable.logo_visa),
name = "visa",
@ -402,12 +398,12 @@ private fun CardLogoRow() {
name = "master",
)
CardLogo(
icon = painterResource(R.drawable.logo_union_pay),
name = "union",
icon = painterResource(R.drawable.logo_mpu),
name = "master",
)
CardLogo(
icon = painterResource(R.drawable.logo_jcb),
name = "jcb",
icon = painterResource(R.drawable.logo_union_pay),
name = "master",
)
}
}

View File

@ -61,8 +61,10 @@ class CardWaitingViewModel(
retryCounter = 0
fallbackCounter = SystemParamsOperation.getInstance().fallbackCounter
fallbackEnabled = SystemParamsOperation.getInstance().fallbackEnabled
val isSaleTransaction = sharedViewModel.transactionsType.value == TransactionsType.SALE
_uiState.update {
it.copy(canGoBack = true)
it.copy(canGoBack = !isSaleTransaction)
}
if (sharedViewModel.transactionsType.value == TransactionsType.REFUND) {

View File

@ -23,8 +23,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.POSUtil
@Composable
internal fun HistoryTransactionCard(
@ -54,7 +54,7 @@ internal fun HistoryTransactionCard(
style = MaterialTheme.typography.titleSmall
)
Text(
text = displayAmountWithSign(payDetail),
text = POSUtil.getInstance().formatAmount(payDetail.amount),
style = MaterialTheme.typography.titleSmall
)
}
@ -62,7 +62,7 @@ internal fun HistoryTransactionCard(
Text("Trace ${payDetail.voucherNo.ifBlank { "-" }}", style = MaterialTheme.typography.bodySmall)
Text("RRN ${payDetail.referNo.ifBlank { "-" }}", style = MaterialTheme.typography.bodySmall, color = Color.Gray)
Text(
"${convertDateString(payDetail.transDate)} ${payDetail.transTime}",
"${payDetail.transDate} ${payDetail.transTime}",
style = MaterialTheme.typography.bodySmall,
color = Color.Gray
)
@ -85,13 +85,13 @@ internal fun HistoryTransactionDetailCard(payDetail: PayDetail) {
color = Color.LegacyRed,
style = MaterialTheme.typography.titleMedium
)
SummaryLine("Trace", payDetail.voucherNo)
SummaryLine("Amount", POSUtil.getInstance().formatAmount(payDetail.amount))
SummaryLine("Card No", payDetail.cardNo)
SummaryLine("Reference", payDetail.referNo)
SummaryLine("Approval", payDetail.approvalCode.orEmpty())
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))
}
}

View File

@ -46,7 +46,6 @@ internal fun displayTransactionTitle(payDetail: PayDetail): String {
internal fun isValidForReprint(payDetail: PayDetail): Boolean {
if (payDetail.transactionType == TransactionsType.SETTLEMENT.value) return false
if (payDetail.transactionType == TransactionsType.REVERSAL.value) return false
if (payDetail.transactionType == TransactionsType.WAVEPAY.value && payDetail.qrTransStatus != 1) {
return false
}
@ -60,8 +59,14 @@ internal fun filterReprintAnyTransactions(
transactions: List<PayDetail>,
hostType: HostType
): List<PayDetail> {
val yesterday = POSUtil.getInstance().yesterdayDate
val dateFormat = SimpleDateFormat("dd/MM/yy", Locale.getDefault())
return transactions.filter { pay ->
if (!isValidForReprint(pay)) return@filter false
val transDate = parseTransDate(pay.transDate, dateFormat) ?: return@filter false
if (yesterday.compareTo(transDate) > 0) return@filter false
if (pay.transactionType == TransactionsType.SETTLEMENT.value) return@filter false
if (pay.transactionType == TransactionsType.REVERSAL.value) return@filter false
when (hostType) {
HostType.MPU ->
@ -86,7 +91,10 @@ internal fun filterDetailReportTransactions(
transactions: List<PayDetail>,
hostType: HostType
): List<PayDetail> {
val yesterday = POSUtil.getInstance().yesterdayDate
return transactions.filter { pay ->
if (POSUtil.getInstance().getDateByString(pay.transDate).before(yesterday)) return@filter false
if (pay.transactionType == TransactionsType.REVERSAL.value) return@filter false
if (pay.transactionType == TransactionsType.SETTLEMENT.value) return@filter false
@ -109,20 +117,10 @@ internal fun filterDetailReportTransactions(
}
}
internal fun displayAmountWithSign(payDetail: PayDetail): String {
val prefix = if (payDetail.transactionType == TransactionsType.VOID.value ||
payDetail.transactionType == TransactionsType.REFUND.value ||
payDetail.transactionType == TransactionsType.WAVEPAY_REFUND.value ||
payDetail.transactionType == TransactionsType.CASH_OUT.value
) "-" else ""
return prefix + POSUtil.getInstance().formatAmount(payDetail.amount)
}
internal fun detailReportSignedAmount(payDetail: PayDetail): Long {
val isNegative = payDetail.transactionType == TransactionsType.VOID.value ||
payDetail.transactionType == TransactionsType.REFUND.value ||
payDetail.transactionType == TransactionsType.WAVEPAY_REFUND.value ||
payDetail.transactionType == TransactionsType.CASH_OUT.value
payDetail.transactionType == TransactionsType.WAVEPAY_REFUND.value
return if (isNegative) -payDetail.amount else payDetail.amount
}

View File

@ -2,6 +2,7 @@ package com.mob.utsmyanmar.ui.history_functions
import com.utsmyanmar.paylibs.model.PayDetail
import com.utsmyanmar.paylibs.model.SettleData
import com.utsmyanmar.paylibs.utils.POSUtil
import com.utsmyanmar.paylibs.utils.enums.HostType
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType
import java.text.ParseException
@ -30,7 +31,9 @@ internal fun buildSummarySettleData(
start: Date,
end: Date,
): SettleData {
val dateFormat = SimpleDateFormat("dd/MM/yy", Locale.getDefault())
val dateTimeFormat = SimpleDateFormat("dd/MM/yy HH:mm", Locale.getDefault())
val yesterday = POSUtil.getInstance().yesterdayDate
val wave = SummaryCounter()
val waveRefund = SummaryCounter()
@ -44,6 +47,14 @@ internal fun buildSummarySettleData(
val refund = SummaryCounter()
for (pay in transactions) {
val transDate = try {
dateFormat.parse(pay.transDate)
} catch (_: ParseException) {
continue
} ?: continue
if (yesterday.after(transDate)) continue
val currentDate = try {
dateTimeFormat.parse("${pay.transDate} ${pay.transTime}")
} catch (_: ParseException) {

View File

@ -1,13 +1,8 @@
package com.mob.utsmyanmar.ui.history_functions
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.clickable
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
@ -15,20 +10,11 @@ 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.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.rounded.Backspace
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
@ -37,27 +23,12 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.mob.utsmyanmar.ui.components.NumericKeypad
import com.mob.utsmyanmar.ui.preview.P2Preview
import com.mob.utsmyanmar.ui.preview.P3Preview
import com.mob.utsmyanmar.ui.components.appbar.AppBar
import com.mob.utsmyanmar.ui.theme.Color
import com.mob.utsmyanmar.ui.theme.MOBPOSTheme
import com.utsmyanmar.paylibs.model.PayDetail
import com.utsmyanmar.paylibs.utils.enums.HostType
private val searchKeys = listOf(
listOf("1", "2", "3"),
listOf("4", "5", "6"),
listOf("7", "8", "9"),
listOf("C", "0", "\u232B")
)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ReprintAnyTransactionScreen(
viewModel: HistoryViewModel,
@ -67,7 +38,6 @@ fun ReprintAnyTransactionScreen(
) {
val allTransactions by viewModel.getTransactionHistory().observeAsState(emptyList())
var searchQuery by remember { mutableStateOf("") }
var isFocused by remember { mutableStateOf(false) }
val filtered = remember(allTransactions, hostType, searchQuery) {
val base = filterReprintAnyTransactions(allTransactions, hostType)
@ -77,32 +47,10 @@ fun ReprintAnyTransactionScreen(
Scaffold(
containerColor = Color.IvoryBeige,
topBar = {
TopAppBar(
title = {
Column {
Text(
text = "Reprint Transaction",
style = MaterialTheme.typography.titleMedium
)
Text(
text = hostTypeLabel(hostType),
style = MaterialTheme.typography.labelSmall,
color = Color.Gray
)
}
},
navigationIcon = {
IconButton(onClick = onBack) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back",
tint = Color.LegacyRed
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = Color.IvoryBeige
)
AppBar(
title = "Transactions",
icon = Icons.AutoMirrored.Filled.ArrowBack,
onIconClick = onBack
)
}
) { paddingValues ->
@ -110,154 +58,37 @@ fun ReprintAnyTransactionScreen(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.padding(horizontal = 16.dp)
.padding(16.dp)
) {
SearchInputBox(
query = searchQuery,
isFocused = isFocused,
onFocus = { isFocused = true }
OutlinedTextField(
value = searchQuery,
onValueChange = { searchQuery = it },
modifier = Modifier.fillMaxWidth(),
label = { Text("Trace (6) or RRN (12)") },
singleLine = true
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "${filtered.size} transaction(s) found",
style = MaterialTheme.typography.labelSmall,
color = Color.Gray,
modifier = Modifier.padding(horizontal = 4.dp)
)
Spacer(modifier = Modifier.height(8.dp))
Spacer(modifier = Modifier.height(12.dp))
if (filtered.isEmpty()) {
Box(
modifier = Modifier.weight(1f),
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = if (allTransactions.isEmpty()) "No transactions available" else "No matching transactions",
color = Color.Gray,
style = MaterialTheme.typography.bodyMedium
)
Text("No transactions found.", color = Color.Gray)
}
} else {
LazyColumn(
modifier = Modifier.weight(1f),
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(filtered, key = { "${it.voucherNo}_${it.referNo}_${it.PID}" }) { payDetail ->
HistoryTransactionCard(payDetail) {
isFocused = false
onReprintTransaction(prepareReprintPayDetail(payDetail))
}
}
}
}
AnimatedVisibility(
visible = isFocused,
enter = slideInVertically(initialOffsetY = { it }),
exit = slideOutVertically(targetOffsetY = { it })
) {
NumericKeypad(
keys = searchKeys,
modifier = Modifier
.fillMaxWidth()
.height(280.dp)
.padding(top = 8.dp),
onKeyClick = { key ->
when (key) {
"C" -> searchQuery = ""
"\u232B" -> searchQuery = searchQuery.dropLast(1)
else -> if (searchQuery.length < 12) searchQuery += key
}
}
)
}
}
}
}
@Composable
private fun SearchInputBox(
query: String,
isFocused: Boolean,
onFocus: () -> Unit,
) {
Card(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onFocus),
colors = CardDefaults.cardColors(containerColor = Color.White),
shape = RoundedCornerShape(12.dp),
elevation = CardDefaults.cardElevation(defaultElevation = if (isFocused) 6.dp else 2.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 14.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = if (isFocused) "Trace or RRN" else "Tap to search by Trace or RRN",
style = MaterialTheme.typography.bodySmall,
color = if (isFocused) Color.LegacyRed else Color.Gray
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = query.ifEmpty { "_" },
color = Color.LegacyRed,
style = MaterialTheme.typography.headlineSmall.copy(
fontWeight = FontWeight.Bold,
letterSpacing = 2.sp
)
)
}
if (query.isNotEmpty()) {
Card(
shape = RoundedCornerShape(18.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
modifier = Modifier
.shadow(2.dp, RoundedCornerShape(18.dp))
.clickable { }
) {
Icon(
imageVector = Icons.AutoMirrored.Rounded.Backspace,
contentDescription = "Delete",
tint = Color.LegacyRed,
modifier = Modifier.padding(horizontal = 14.dp, vertical = 12.dp)
)
}
}
}
}
}
@P2Preview
@P3Preview
@Composable
private fun ReprintAnyTransactionScreenPreview() {
MOBPOSTheme {
Box(modifier = Modifier.height(600.dp)) {
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = Color.White)
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "SALE",
color = Color.LegacyRed,
style = MaterialTheme.typography.titleSmall
)
Spacer(modifier = Modifier.height(4.dp))
Text("Trace 000001", style = MaterialTheme.typography.bodySmall)
Text("RRN 123456789012", style = MaterialTheme.typography.bodySmall, color = Color.Gray)
}
}
}
}
}

View File

@ -171,16 +171,16 @@ fun InputAmount(
Text("Cancel")
}
val isAmountValid = amount.toDoubleOrNull()?.let { it > 0.0 } == true
Button(
onClick = {
if (amount.isNotEmpty()) {
onChargeClick(amount)
}
},
modifier = Modifier
.weight(1f)
.height(56.dp),
enabled = isAmountValid,
enabled = amount.isNotEmpty(),
shape = RoundedCornerShape(8.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Color.LegacyRed,

View File

@ -3,7 +3,6 @@ package com.mob.utsmyanmar.ui.print_receipt
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
@ -33,26 +32,20 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.mob.utsmyanmar.ui.components.appbar.AppBar
import com.mob.utsmyanmar.ui.history_functions.displayTransactionStatus
import com.mob.utsmyanmar.ui.history_functions.displayTransactionTitle
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.mob.utsmyanmar.ui.transaction_result.TransactionResultState
import com.mob.utsmyanmar.ui.transaction_result.TransactionResultViewModel
import com.mob.utsmyanmar.utils.convertDateString
import com.mob.utsmyanmar.viewmodel.SharedViewModel
import com.utsmyanmar.paylibs.model.PayDetail
import com.utsmyanmar.paylibs.utils.POSUtil
import com.utsmyanmar.paylibs.utils.PrintStatus
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType
import kotlinx.coroutines.launch
private enum class ReceiptCopy(val label: String) {
@ -66,32 +59,9 @@ fun PrintReceiptScreen(
sharedViewModel: SharedViewModel,
onPrint: () -> Unit,
onDone: () -> Unit,
onPrintFinished: () -> Unit,
onPrintFinished : () -> Unit,
) {
val state by transactionResultViewModel.state.collectAsStateWithLifecycle()
val isReprint = sharedViewModel.isReprint.value ?: false
PrintReceiptContent(
state = state,
isReprint = isReprint,
onPrint = onPrint,
onDone = onDone,
onPrintFinished = onPrintFinished,
onSetPrintStatus = { sharedViewModel.setPrintStatus(it) },
onResetReprint = { sharedViewModel.isReprint.value = false },
)
}
@Composable
fun PrintReceiptContent(
state: TransactionResultState,
isReprint: Boolean,
onPrint: () -> Unit,
onDone: () -> Unit,
onPrintFinished: () -> Unit,
onSetPrintStatus: (PrintStatus) -> Unit,
onResetReprint: () -> Unit
) {
val scope = rememberCoroutineScope()
val receiptOffsetY = remember { Animatable(0f) }
val merchant_copy_autoprint = true
@ -112,11 +82,14 @@ fun PrintReceiptContent(
)
}
fun isReprintMode() = sharedViewModel.isReprint.value == true
fun doPrint(copy: ReceiptCopy, onFinished: (printFinished: ReceiptCopy) -> Unit) {
val actualCopy = if (isReprint && copy == ReceiptCopy.MERCHANT) ReceiptCopy.CUSTOMER else copy
val reprint = isReprintMode()
val actualCopy = if (reprint && copy == ReceiptCopy.MERCHANT) ReceiptCopy.CUSTOMER else copy
currentCopy = actualCopy
onSetPrintStatus(
sharedViewModel.setPrintStatus(
if (actualCopy == ReceiptCopy.MERCHANT)
PrintStatus.FIRST_PRINT
else
@ -127,7 +100,7 @@ fun PrintReceiptContent(
isPrinting = true
animatePrint()
isPrinting = false
val upcomingCopy = if (isReprint) ReceiptCopy.CUSTOMER
val upcomingCopy = if (reprint) ReceiptCopy.CUSTOMER
else if (actualCopy == ReceiptCopy.MERCHANT) ReceiptCopy.CUSTOMER
else ReceiptCopy.CUSTOMER
@ -139,13 +112,14 @@ fun PrintReceiptContent(
}
LaunchedEffect(Unit) {
if (isReprint) {
val reprint = isReprintMode()
if (reprint) {
currentCopy = ReceiptCopy.CUSTOMER
nextCopy = ReceiptCopy.CUSTOMER
}
onResetReprint()
sharedViewModel.isReprint.value = false
when {
merchant_copy_autoprint && !isReprint -> doPrint(ReceiptCopy.MERCHANT) { }
merchant_copy_autoprint && !reprint -> doPrint(ReceiptCopy.MERCHANT) { }
customer_copy_autoprint -> doPrint(ReceiptCopy.CUSTOMER) { }
}
}
@ -235,7 +209,6 @@ fun ReceiptPreview(
copyLabel: String,
modifier: Modifier = Modifier
) {
val isPreview = LocalInspectionMode.current
Column(
modifier = modifier
.fillMaxWidth()
@ -266,21 +239,17 @@ fun ReceiptPreview(
)
Spacer(modifier = Modifier.height(16.dp))
ReceiptDivider()
ReceiptRow("Date", convertDateString(state.payDetail?.transDate.toString()))
ReceiptRow("Time", state.payDetail?.transTime.toString())
ReceiptRow("Merchant Name", state.payDetail?.merchantName.toString())
ReceiptRow("TID", state.payDetail?.terminalNo.toString())
ReceiptRow("Trace No", state.payDetail?.voucherNo.toString())
ReceiptRow("Ref No", state.payDetail?.referNo.toString())
ReceiptRow("Approval Code", state.payDetail?.approvalCode.toString())
ReceiptRow("Transaction Type", state.payDetail?.let(::displayTransactionTitle).orEmpty())
ReceiptRow("Amount", POSUtil.getInstance().getDecimalAmountSeparatorFormat(state.payDetail?.amount?:0).let { amt ->
val prefix = POSUtil.getInstance().checkMinusSign(state.payDetail?.transactionType ?: 0)
if (prefix.isEmpty() && (state.payDetail?.transactionType ?: 0) == TransactionsType.CASH_OUT.value) "-$amt" else "$prefix$amt"
})
ReceiptRow("Merchant", state.payDetail?.merchantName.toString() )
ReceiptRow("Terminal ID", state.payDetail?.terminalNo.toString() )
ReceiptRow("Trace No", state.payDetail?.voucherNo.toString() )
ReceiptRow("Transaction", state.payDetail?.let(::displayTransactionTitle).orEmpty())
ReceiptRow("Amount", POSUtil.getInstance().getDecimalAmountSeparatorFormat(state.payDetail?.amount?:0))
ReceiptRow("Card Type", state.payDetail?.accountType.toString())
ReceiptRow("Card No", state.payDetail?.cardNo.toString())
ReceiptRow("Status", if (isPreview) "SUCCESS" else state.payDetail?.let(::displayTransactionStatus).orEmpty())
ReceiptRow("Status", state.payDetail?.let(::displayTransactionStatus).orEmpty())
ReceiptRow("Date", state.payDetail?.transDate.toString() )
ReceiptRow("Time", state.payDetail?.transTime.toString())
ReceiptRow("Ref No", state.payDetail?.referNo.toString())
ReceiptDivider()
Spacer(modifier = Modifier.height(8.dp))
Text(
@ -323,63 +292,25 @@ fun ReceiptRow(
@Composable
fun ReceiptDivider() {
Canvas(
Text(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 10.dp)
.height(2.dp)
) {
// Convert dp to pixels so it scales perfectly on all screen sizes
val dotRadius = 1.5.dp.toPx()
val dotSpacing = 6.dp.toPx()
val totalWidth = size.width
var currentX = 0f
// Loop across the width of the screen and draw individual circles
while (currentX < totalWidth) {
drawCircle(
color = Color.Gray,
radius = dotRadius,
center = Offset(x = currentX, y = size.height / 2)
.padding(vertical = 10.dp),
text = "--------------------------------",
color = Color.Gray
)
// Move X forward by the size of the dot + the space we want
currentX += (dotRadius * 2) + dotSpacing
}
}
}
@P2Preview
@P3Preview
@Preview(showBackground = true, showSystemUi = true)
@Composable
private fun PrintReceiptScreenPreview() {
val samplePayDetail = PayDetail().apply {
merchantName = "Sample Merchant"
terminalNo = "12345678"
voucherNo = "000001"
amount = 100000
cardNo = "1234********5678"
transDate = "25-08-26"
transTime = "10:30:00"
referNo = "123456789012"
transType = "SALE"
tradeAnswerCode = "00"
approvalCode = "123456"
}
val sampleState = TransactionResultState(
payDetail = samplePayDetail
)
MOBPOSTheme {
PrintReceiptContent(
state = sampleState,
isReprint = false,
fun PreviewPrintReceiptScreen() {
MaterialTheme {
PrintReceiptScreen(
sharedViewModel = hiltViewModel(),
transactionResultViewModel = hiltViewModel(),
onPrint = {},
onDone = {},
onPrintFinished = {},
onSetPrintStatus = {},
onResetReprint = {}
onPrintFinished = {}
)
}
}

View File

@ -28,7 +28,6 @@ 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.utils.convertDateString
import com.utsmyanmar.paylibs.model.PayDetail
import com.utsmyanmar.paylibs.utils.POSUtil
@ -127,18 +126,19 @@ private fun TransactionDetailsCard(transaction: PayDetail) {
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Text(
text = "Void Sale",
text = "Previous Sale",
color = Color.LegacyRed,
fontWeight = FontWeight.Bold,
fontSize = 16.sp
)
DetailRow("Date", convertDateString(transaction.transDate))
DetailRow("Time", transaction.transTime)
DetailRow("Trace", transaction.voucherNo)
DetailRow("Amount", POSUtil.getInstance().formatAmount(transaction.amount))
DetailRow("Trace No", transaction.voucherNo)
DetailRow("Reference No", transaction.referNo)
DetailRow("Approval Code", transaction.approvalCode.orEmpty())
// DetailRow("Card No", transaction.cardNo)
DetailRow("Card No", transaction.cardNo)
DetailRow("Reference", transaction.referNo)
DetailRow("Approval", transaction.approvalCode.orEmpty())
DetailRow("Date", transaction.transDate)
DetailRow("Time", transaction.transTime)
}
}
}

View File

@ -24,7 +24,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.mob.utsmyanmar.ui.preview.P2Preview
import com.mob.utsmyanmar.ui.theme.Color
import com.mob.utsmyanmar.utils.convertDateString
import com.mob.utsmyanmar.utils.toFormattedDate
import com.mob.utsmyanmar.utils.toFormattedTime
import com.utsmyanmar.paylibs.model.PayDetail
import com.utsmyanmar.paylibs.print.PrintReceipt
@ -82,7 +82,7 @@ fun TransactionResultScreen(
Spacer(modifier = Modifier.height(16.dp))
InfoCard(
date = convertDateString(state.payDetail?.transDate.toString()),
date = "${state.payDetail?.transDate?.toFormattedDate()}",
time = "${state.payDetail?.transTime?.toFormattedTime()}",
transactionId = state.payDetail?.referNo.toString()
)

View File

@ -2,7 +2,6 @@ package com.mob.utsmyanmar.utils
import android.annotation.SuppressLint
@SuppressLint("DefaultLocale")
fun Long?.formatAmount(): String {
val amount = (this ?: 0L) / 100.0
@ -19,7 +18,3 @@ fun String?.maskCardNumber(): String {
}
return maskedPan.toString()
}
fun convertDateString(dateStr: String): String {
return dateStr.replace(Regex("/(\\d{2})$"), "/20$1")
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

View File

@ -575,7 +575,7 @@ public class PrintReceipt {
String receiptFooter = SystemParamsOperation.getInstance().getReceiptFooter();
// String finalVersion = SystemParamsOperation.getInstance().getFinalVersion();
String finalVersion = SystemParamsOperation.getInstance().getFinalVersion();
sunmiPrinterService.sendRAWData(boldOn(), null);
sunmiPrinterService.setAlignment(1, null);
@ -596,7 +596,7 @@ public class PrintReceipt {
} else {
sunmiPrinterService.printText("***** CUSTOMER COPY *****" + "\n", null);
}
// sunmiPrinterService.printText(finalVersion + "\n", null);
sunmiPrinterService.printText(finalVersion + "\n", null);
sunmiPrinterService.printText(receiptFooter + "\n", null);
} catch (RemoteException e) {

View File

@ -155,11 +155,6 @@ public class PrintUtils {
}
}
public String toFullYearDate(String dateStr) {
if (TextUtils.isEmpty(dateStr)) return dateStr;
return dateStr.replaceFirst("/(\\d{2})$", "/20$1");
}
public String getFormatTransactionTime(String transTime) {
if (!TextUtils.equals("", transTime)) {
return transTime.substring(0, 2) + ":" + transTime.substring(2, 4) + ":" + transTime.substring(4, 6);

View File

@ -202,11 +202,11 @@ public abstract class BaseXPrint {
try {
String receiptFooter = SystemParamsOperation.getInstance().getReceiptFooter();
// String finalVersion = SystemParamsOperation.getInstance().getFinalVersion();
String finalVersion = SystemParamsOperation.getInstance().getFinalVersion();
sunmiPrinterService.setFontSize(textSize, innerResultCallback);
// sunmiPrinterService.printText("***** MERCHANT COPY *****" + "\n", innerResultCallback);
// sunmiPrinterService.printText(finalVersion + "\n", innerResultCallback);
sunmiPrinterService.printText(finalVersion + "\n", innerResultCallback);
sunmiPrinterService.printText(receiptFooter + "\n", innerResultCallback);
} catch (RemoteException e) {
e.printStackTrace();
@ -267,7 +267,6 @@ public abstract class BaseXPrint {
}
sunmiPrinterService.printText("REF NUM : " + payDetail.getReferNo() + "\n", innerResultCallback);
sunmiPrinterService.printText("TRACE NO : " + payDetail.getVoucherNo() + "\n", innerResultCallback);
sunmiPrinterService.printText("APR CODE : " + payDetail.getApprovalCode() + "\n", innerResultCallback);
// sunmiPrinterService.printColumnsString(new String[]{"REF NUM", ":" + payDetail.getReferNo()}, new int[]{1, 2}, new int[]{0, 0}, innerResultCallback);
// sunmiPrinterService.printColumnsString(new String[]{"TRACE No", ":" + payDetail.getVoucherNo()}, new int[]{1, 2}, new int[]{0, 0}, innerResultCallback);
@ -376,7 +375,7 @@ public abstract class BaseXPrint {
try {
sunmiPrinterService.setFontSize(20, innerResultCallback);
sunmiPrinterService.setAlignment(0, innerResultCallback);
sunmiPrinterService.printColumnsString(new String[]{"DATE :" + PrintUtils.getInstance().toFullYearDate(payDetail.getTransDate()), "TIME: " + payDetail.getTransTime()}, new int[]{2, 2}, new int[]{0, 2}, innerResultCallback);
sunmiPrinterService.printColumnsString(new String[]{"DATE :" + payDetail.getTransDate(), "TIME: " + payDetail.getTransTime()}, new int[]{2, 2}, new int[]{0, 2}, innerResultCallback);
if(Objects.equals(payDetail.getAccountType(), CardScheme.MPU_CTLS.name) || Objects.equals(payDetail.getAccountType(), CardScheme.UPI.name) ) {
hostName = HOST_NAME_MPU;
} else {
@ -637,7 +636,7 @@ public abstract class BaseXPrint {
for (PayDetail pay: lists) {
boolean isNeedMinusSign = pay.getTransactionType() == TransactionsType.VOID.value || pay.getTransactionType() == TransactionsType.REFUND.value || pay.getTransactionType() == TransactionsType.WAVEPAY_REFUND.value || pay.getTransactionType() == TransactionsType.PRE_AUTH_COMPLETE_VOID.value || pay.getTransactionType() == TransactionsType.CASH_OUT.value;
boolean isNeedMinusSign = pay.getTransactionType() == TransactionsType.VOID.value || pay.getTransactionType() == TransactionsType.REFUND.value || pay.getTransactionType() == TransactionsType.WAVEPAY_REFUND.value || pay.getTransactionType() == TransactionsType.PRE_AUTH_COMPLETE_VOID.value;
if(hostType == HostType.MPU || hostType == HostType.VISA_MASTER) {
sunmiPrinterService.printColumnsString(new String[]{pay.getAccountType() , PrintUtils.getInstance().maskCardNumberPciDss(pay.getCardNo())}, new int[]{2, 2}, new int[]{0, 2}, innerResultCallback);
@ -719,7 +718,7 @@ public abstract class BaseXPrint {
String receiptFooter = SystemParamsOperation.getInstance().getReceiptFooter();
// String finalVersion = SystemParamsOperation.getInstance().getFinalVersion();
String finalVersion = SystemParamsOperation.getInstance().getFinalVersion();
sunmiPrinterService.sendRAWData(boldOn(), innerResultCallback);
sunmiPrinterService.setAlignment(1, innerResultCallback);
@ -762,7 +761,7 @@ public abstract class BaseXPrint {
} else {
sunmiPrinterService.printText("***** CUSTOMER COPY *****" + "\n", innerResultCallback);
}
// sunmiPrinterService.printText(finalVersion + "\n", innerResultCallback);
sunmiPrinterService.printText(finalVersion + "\n", innerResultCallback);
sunmiPrinterService.printText(receiptFooter + "\n", innerResultCallback);
} catch (RemoteException e) {
@ -895,7 +894,7 @@ public abstract class BaseXPrint {
try {
sunmiPrinterService.setFontSize(18, innerResultCallback);
sunmiPrinterService.setAlignment(0, innerResultCallback);
sunmiPrinterService.printColumnsString(new String[]{"DATE :" + PrintUtils.getInstance().toFullYearDate(payDetail.getTransDate()), "TIME: " + payDetail.getTransTime()}, new int[]{2, 2}, new int[]{0, 2}, innerResultCallback);
sunmiPrinterService.printColumnsString(new String[]{"DATE :" + payDetail.getTransDate(), "TIME: " + payDetail.getTransTime()}, new int[]{2, 2}, new int[]{0, 2}, innerResultCallback);
if (payDetail.getTransactionType() == TransactionsType.WAVEPAY.value && payDetail.getQrTransStatus() == 1) {
sunmiPrinterService.printColumnsString(new String[]{"TRACE NO :" + traceNum, "INV NO: " + invoiceNo}, new int[]{2, 2}, new int[]{0, 2}, innerResultCallback);
} else if (!payDetail.getTransType().equals(SETTLEMENT) && !payDetail.getTransType().equals(SUMMARY) && payDetail.getTransactionType() != TransactionsType.WAVEPAY_REFUND.value && payDetail.getTransactionType() != TransactionsType.WAVEPAY.value) {

View File

@ -48,14 +48,14 @@ public class PrintXImpl extends BaseXPrint implements PrintX {
opts.inJustDecodeBounds = true;
// Step 1: Only read dimensions first
BitmapFactory.decodeResource(resources, R.drawable.logo_mob_receipt, opts);
BitmapFactory.decodeResource(resources, R.drawable.mob_print_logo, opts);
// Step 2: Calculate sample size so width <= 384px
opts.inSampleSize = calculateInSampleSize(opts, 384, 384);
opts.inJustDecodeBounds = false;
// Step 3: Decode scaled-down bitmap
bitmap = BitmapFactory.decodeResource(resources, R.drawable.logo_mob_receipt, opts);
bitmap = BitmapFactory.decodeResource(resources, R.drawable.mob_print_logo, opts);
}
@Override
@ -172,7 +172,6 @@ public class PrintXImpl extends BaseXPrint implements PrintX {
try {
setHeight(0x12);
printLogo();
sunmiPrinterService.lineWrap(1, innerResultCallback);
printMerchantHeader();
printTransHeader(payDetail);
printTransDetailWave(payDetail);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB