Compare commits

..

8 Commits

28 changed files with 399 additions and 405 deletions

View File

@ -5,9 +5,6 @@ plugins {
id("com.google.dagger.hilt.android")
}
// Set default build variant
project.ext.set("android.defaultPublishConfig", "uatDebug")
android {
namespace = "com.mob.utsmyanmar"
compileSdk {
@ -62,6 +59,14 @@ android {
"proguard-rules.pro"
)
}
debug {
isMinifyEnabled = false
isDebuggable = true
// proguardFiles(
// getDefaultProguardFile("proguard-android-optimize.txt"),
// "proguard-rules.pro"
// )
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
@ -120,4 +125,6 @@ dependencies {
implementation(project(":ecr"))
implementation(project(":xpay"))
implementation(project(":cmhl"))
implementation("se.warting.signature:signature-pad:0.2.0")
}

View File

@ -16,14 +16,13 @@ import com.utsmyanmar.ecr.ECRHelper
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
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
class CardWaitingViewModel(
private val cardReadViewModel: CardReaderViewModel,

View File

@ -1,5 +1,8 @@
package com.mob.utsmyanmar.ui.dashboard
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountBalanceWallet
import androidx.compose.material.icons.filled.CreditCard
@ -7,7 +10,6 @@ import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.LockOpen
import androidx.compose.material.icons.filled.Replay
import androidx.compose.material.icons.filled.SwapHoriz
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@ -15,6 +17,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.mob.utsmyanmar.R
import com.mob.utsmyanmar.ui.theme.Color
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation
class DashboardMenuItem(
val title: String,
@ -22,13 +25,26 @@ class DashboardMenuItem(
val onClick: () -> Unit
)
val settlementEnabled = false
val voidEnabled = true
val refundEnabled = true
val preAuthEnabled = true
val preAuthVoidEnabled = true
val preAuthCompleteEnabled = true
val preAuthCompleteVoidEnabled = true
val cashAdvEnabled = true
fun buildMenuItems(
onNavigateAmount: (String) -> Unit,
onNavigateSignOn: () -> Unit,
onNavigateSettlement: () -> Unit,
onNavigateHistory: () -> Unit,
onNavigateAction: (String) -> Unit
): List<DashboardMenuItem> = listOf(
): List<DashboardMenuItem> = buildList {
val mmqrEnabled = SystemParamsOperation.getInstance().mmqrStatus
add(
DashboardMenuItem(
"Sale",
{
@ -38,7 +54,26 @@ fun buildMenuItems(
modifier = Modifier.size(40.dp),
tint = Color.LegacyRed
)
}) { onNavigateAmount("Sale") },
}
) { onNavigateAmount("Sale") }
)
if (mmqrEnabled) {
add(
DashboardMenuItem(
"MMQR",
{
Image(
painter = painterResource(R.drawable.ic_mmqr_logo),
contentDescription = null,
modifier = Modifier.height(48.dp)
)
}
) { }
)
}
add(
DashboardMenuItem(
"History",
{
@ -48,7 +83,11 @@ fun buildMenuItems(
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}) { onNavigateHistory() },
}
) { onNavigateHistory() }
)
add(
DashboardMenuItem(
"Sign On",
{
@ -58,7 +97,12 @@ fun buildMenuItems(
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}) { onNavigateSignOn() },
}
) { onNavigateSignOn() }
)
if (settlementEnabled) {
add(
DashboardMenuItem(
"Settlement",
{
@ -68,7 +112,13 @@ fun buildMenuItems(
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}) { onNavigateSettlement() },
}
) { onNavigateSettlement() }
)
}
if (voidEnabled) {
add(
DashboardMenuItem(
"Void",
{
@ -78,7 +128,13 @@ fun buildMenuItems(
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}) { onNavigateAction("Void") },
}
) { onNavigateAction("Void") }
)
}
if (refundEnabled) {
add(
DashboardMenuItem(
"Refund",
{
@ -88,7 +144,13 @@ fun buildMenuItems(
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}) { onNavigateAction("Refund") },
}
) { onNavigateAction("Refund") }
)
}
if (preAuthEnabled) {
add(
DashboardMenuItem(
"Pre-Auth",
{
@ -98,7 +160,13 @@ fun buildMenuItems(
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}) { onNavigateAmount("Pre-Auth") },
}
) { onNavigateAmount("Pre-Auth") }
)
}
if (preAuthVoidEnabled) {
add(
DashboardMenuItem(
"Pre-Auth Void",
{
@ -108,7 +176,13 @@ fun buildMenuItems(
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}) { onNavigateAction("Pre-Auth Void") },
}
) { onNavigateAction("Pre-Auth Void") }
)
}
if (preAuthCompleteEnabled) {
add(
DashboardMenuItem(
"Pre-Auth Complete",
{
@ -118,7 +192,13 @@ fun buildMenuItems(
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}) { onNavigateAction("Pre-Auth Complete") },
}
) { onNavigateAction("Pre-Auth Complete") }
)
}
if (preAuthCompleteVoidEnabled) {
add(
DashboardMenuItem(
"Pre-Auth Comp Void",
{
@ -128,7 +208,13 @@ fun buildMenuItems(
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}) { onNavigateAction("Pre-Auth Complete Void") },
}
) { onNavigateAction("Pre-Auth Complete Void") }
)
}
if (cashAdvEnabled) {
add(
DashboardMenuItem(
"Cash Adv",
{
@ -138,5 +224,10 @@ fun buildMenuItems(
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}) { onNavigateAmount("Cash Adv") },
}
) { onNavigateAmount("Cash Adv") }
)
}
}

View File

@ -23,6 +23,9 @@ import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material.icons.filled.Sync
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@ -47,6 +50,7 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.mob.utsmyanmar.R
import com.mob.utsmyanmar.ui.components.appbar.AppBar
import com.mob.utsmyanmar.ui.device_info.DeviceInfoUiState
@ -176,6 +180,29 @@ fun DashboardScreen2(
)
}
if (dashboardUiState.isSyncing) {
Dialog(onDismissRequest = {}) {
Card(
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(8.dp)
) {
Column(
modifier = Modifier.padding(32.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
CircularProgressIndicator(color = Color.LegacyRed)
Spacer(Modifier.height(16.dp))
Text(
text = dashboardUiState.syncStatusText,
style = MaterialTheme.typography.bodyMedium,
color = Color.Black
)
}
}
}
}
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {

View File

@ -17,5 +17,7 @@ data class DashboardUiState(
val totalSaleCount: Int = 0,
val totalSaleAmount: Long = 0L,
val settlementStatusText: String = "Pending",
val settlementDateTimeText: String = "--/-- --:--"
val settlementDateTimeText: String = "--/-- --:--",
val isSyncing: Boolean = false,
val syncStatusText: String = "Connecting to TMS server..."
)

View File

@ -72,6 +72,18 @@ class DashboardViewModel @Inject constructor(
.takeIf { it > 0L }
_uiState.update { it.copy(lastSyncTime = time) }
}
fun startSync() {
_uiState.update { it.copy(isSyncing = true, syncStatusText = "Connecting to TMS server...") }
}
fun finishSync(message: String) {
_uiState.update { it.copy(syncStatusText = message) }
viewModelScope.launch {
kotlinx.coroutines.delay(1500)
_uiState.update { it.copy(isSyncing = false) }
}
}
}
private fun PayDetail.toRecord(): TrnxRecord {

View File

@ -108,11 +108,6 @@ fun InputAmount(
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Spacer(modifier = Modifier.height(8.dp))
// Row(
// modifier = Modifier.align(Alignment.CenterHorizontally),
// verticalAlignment = Alignment.Bottom
// ) {
Text(
text = formatAmount(amount.ifEmpty { "0" }),
color = Color.LegacyRed,
@ -127,12 +122,7 @@ fun InputAmount(
modifier = Modifier.padding(end = 10.dp, bottom = 6.dp)
)
}
// }
Spacer(modifier = Modifier.height(14.dp))
Text(
text = supportingText,
color = Color.Gray,
@ -141,8 +131,6 @@ fun InputAmount(
)
}
}
//second container
Column(
modifier = Modifier
.fillMaxWidth()
@ -246,14 +234,15 @@ private fun formatAmount(value: String): String {
val groupedWholePart = "%,d".format(wholePart.toLongOrNull() ?: 0L)
if (!normalized.contains(".")) {
return groupedWholePart
return "$groupedWholePart.00"
}
val decimalPart = normalized.substringAfter(".", "")
return if (normalized.endsWith(".")) {
"$groupedWholePart."
"$groupedWholePart.00"
} else {
"$groupedWholePart.$decimalPart"
val padded = decimalPart.padEnd(2, '0').take(2)
"$groupedWholePart.$padded"
}
}

View File

@ -26,6 +26,7 @@ import com.mob.utsmyanmar.ui.device_info.DeviceInfoViewModel
import com.mob.utsmyanmar.ui.error.TransactionErrorScreen
import com.mob.utsmyanmar.ui.functions.FunctionsScreen
import com.mob.utsmyanmar.ui.history_functions.HistoryRoute
import com.mob.utsmyanmar.ui.history_functions.prepareReprintPayDetail
import com.mob.utsmyanmar.ui.input_amount.AmountRoute
import com.mob.utsmyanmar.ui.password_input.InputPassword
import com.mob.utsmyanmar.ui.password_input.PasswordType
@ -36,7 +37,6 @@ import com.mob.utsmyanmar.ui.processing_card.ProcessingCardRoute
import com.mob.utsmyanmar.ui.processing_card.ProcessingCardViewModel
import com.mob.utsmyanmar.ui.refund_rrn.InputRrnRoute
import com.mob.utsmyanmar.ui.refund_rrn.RRNViewModel
import com.mob.utsmyanmar.ui.history_functions.prepareReprintPayDetail
import com.mob.utsmyanmar.ui.sale_void.TranDetailPage
import com.mob.utsmyanmar.ui.sale_void.VoidTraceScreen
import com.mob.utsmyanmar.ui.sale_void.VoidViewModel
@ -92,7 +92,7 @@ fun AppNavGraph(
val sharedViewModel: SharedViewModel = hiltViewModel(activity)
SignaturePad(
onBack = { navController.popBackStack() },
onConfirm = { _, bitmap, signatureBlob ->
onConfirm = { bitmap, signatureBlob ->
sharedViewModel.signBitmap = bitmap
sharedViewModel.updateSignatureBlob(signatureBlob)
navController.navigate(Routes.TransactionResult.route) {
@ -120,9 +120,31 @@ fun AppNavGraph(
deviceInfoViewModel.loadDeviceInfo()
}
val tmsSetupUiState by tmsSetupViewModel.uiState.collectAsStateWithLifecycle()
LaunchedEffect(tmsSetupViewModel) {
tmsSetupViewModel.onSyncCompleted.collect {
dashboardViewModel.refreshLastSyncTime()
dashboardViewModel.finishSync("Sync completed")
}
}
LaunchedEffect(tmsSetupUiState.isLoading, tmsSetupUiState.isError) {
if (dashboardUiState.isSyncing && !tmsSetupUiState.isLoading) {
if (tmsSetupUiState.isError) {
dashboardViewModel.finishSync(tmsSetupUiState.statusText.ifBlank { "Download failed!" })
}
}
}
LaunchedEffect(tmsSetupUiState.isTerminalDisabled) {
if (tmsSetupUiState.isTerminalDisabled) {
navController.navigate(Routes.TmsSetup.route) {
popUpTo(Routes.Dashboard.route) {
inclusive = true
}
launchSingleTop = true
}
}
}
@ -131,6 +153,7 @@ fun AppNavGraph(
dashboardUiState = dashboardUiState,
deviceInfo = deviceInfoUiState,
onClickLastSync = {
dashboardViewModel.startSync()
tmsSetupViewModel.downloadConfigs()
},
onNavigateRecentTransaction = { record ->
@ -156,7 +179,6 @@ fun AppNavGraph(
launchSingleTop = true
}
}
"Pre-Auth",
"Cash Adv" -> {
navController.navigate(Routes.Amount.createRoute(action)) {
@ -779,7 +801,14 @@ fun AppNavGraph(
}
private fun formatAmountForDisplay(amount: String?): String {
val normalizedAmount = amount.orEmpty()
val value = normalizedAmount.toLongOrNull() ?: return normalizedAmount.ifBlank { "0" }
return "%,d".format(value)
val raw = amount.orEmpty().trim()
if (raw.isBlank()) return "0.00"
if (raw.contains(".")) {
val whole = raw.substringBefore(".").ifEmpty { "0" }
val decimal = raw.substringAfter(".", "")
val grouped = "%,d".format(whole.toLongOrNull() ?: 0L)
return "$grouped.${decimal.padEnd(2, '0').take(2)}"
}
val value = raw.toLongOrNull() ?: return raw
return "%,d.00".format(value)
}

View File

@ -7,7 +7,6 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.mob.utsmyanmar.model.CardTransactionType
import com.mob.utsmyanmar.ui.pinpad.PinPadViewModel
import com.mob.utsmyanmar.utils.MockCardData
import com.mob.utsmyanmar.utils.MockData
import com.mob.utsmyanmar.utils.TransactionUtil
import com.mob.utsmyanmar.viewmodel.CardReaderViewModel

View File

@ -1,15 +1,10 @@
package com.mob.utsmyanmar.ui.signature_pad
import android.graphics.Bitmap
import android.graphics.Paint
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.border
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
@ -24,82 +19,31 @@ import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.graphics.createBitmap
import com.mob.utsmyanmar.ui.components.appbar.AppBar
import com.mob.utsmyanmar.ui.theme.MOBPOSTheme
import se.warting.signaturepad.SignaturePadAdapter
import se.warting.signaturepad.SignaturePadView
import java.io.ByteArrayOutputStream
import android.graphics.Canvas as AndroidCanvas
import android.graphics.Path as AndroidPath
import com.mob.utsmyanmar.ui.theme.Color as AppColor
@Composable
fun SignaturePad(
modifier: Modifier = Modifier,
signatureColor: Color = Color.Black,
onBack: () -> Unit = {},
onConfirm: (SignatureMode, Bitmap?, signatureBlob: ByteArray?) -> Unit = { _, _, _ -> }
onConfirm: (Bitmap?, ByteArray?) -> Unit = { _, _ -> }
) {
val paths = remember { mutableStateListOf<List<Offset>>() }
val currentPath = remember { mutableStateListOf<Offset>() }
var selectedMode by remember { mutableStateOf(SignatureMode.Digital) }
var canvasSize by remember { mutableStateOf(Size.Zero) }
var strokeWidth = 6f
var signaturePadAdapter: SignaturePadAdapter? = null
fun clearSignature() {
paths.clear()
currentPath.clear()
}
val scaledWidth = 300
fun buildBitmap(): Bitmap? {
if (selectedMode == SignatureMode.Manual) return null
if (paths.isEmpty() && currentPath.isEmpty()) return null
if (canvasSize.width <= 0f || canvasSize.height <= 0f) return null
val bitmap =
createBitmap(canvasSize.width.toInt(), canvasSize.height.toInt(), Bitmap.Config.RGB_565)
val canvas = AndroidCanvas(bitmap)
canvas.drawColor(android.graphics.Color.WHITE)
val paint = Paint().apply {
color = android.graphics.Color.BLACK
style = Paint.Style.STROKE
isAntiAlias = true
strokeCap = Paint.Cap.ROUND
strokeJoin = Paint.Join.ROUND
strokeWidth = 10f
}
fun drawStroke(points: List<Offset>) {
if (points.size < 2) return
val path = AndroidPath().apply {
moveTo(points.first().x, points.first().y)
for (index in 1 until points.size) {
lineTo(points[index].x, points[index].y)
}
}
canvas.drawPath(path, paint)
}
paths.forEach(::drawStroke)
drawStroke(currentPath)
return bitmap
fun scaleBitmap(source: Bitmap): Bitmap {
val ratio = scaledWidth.toFloat() / source.width
val height = (source.height * ratio).toInt()
return Bitmap.createScaledBitmap(source, scaledWidth, height, true)
}
fun bitmapToBlob(bitmap: Bitmap): ByteArray {
@ -125,103 +69,20 @@ fun SignaturePad(
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Box(
modifier = Modifier
.fillMaxWidth(0.85f)
.height(180.dp)
.align(Alignment.CenterHorizontally)
.background(
color = if (selectedMode == SignatureMode.Digital) {
Color.White
} else {
Color.White.copy(alpha = 0.55f)
},
shape = RoundedCornerShape(16.dp)
)
.padding(12.dp)
.onSizeChanged { size ->
canvasSize = Size(size.width.toFloat(), size.height.toFloat())
}
.pointerInput(selectedMode) {
if (selectedMode == SignatureMode.Manual) return@pointerInput
detectDragGestures(
onDragStart = { startOffset ->
currentPath.add(startOffset)
},
onDrag = { change, _ ->
change.consume()
currentPath.add(change.position)
},
onDragEnd = {
paths.add(currentPath.toList())
currentPath.clear()
}
)
}
) {
Canvas(modifier = Modifier.fillMaxSize()) {
paths.forEach { points ->
if (points.size > 1) {
val path = Path().apply {
moveTo(points.first().x, points.first().y)
for (i in 1 until points.size) {
lineTo(points[i].x, points[i].y)
}
}
drawPath(
path = path,
color = signatureColor,
style = Stroke(width = strokeWidth)
)
}
}
if (currentPath.size > 1) {
val path = Path().apply {
moveTo(currentPath.first().x, currentPath.first().y)
for (i in 1 until currentPath.size) {
lineTo(currentPath[i].x, currentPath[i].y)
}
}
drawPath(
path = path,
color = signatureColor,
style = Stroke(width = strokeWidth)
)
}
}
}
Column(
SignaturePadView(
onReady = { signaturePadAdapter = it },
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
) {
SignatureModeOption(
title = "Manual Sign",
subtitle = "Signature on Paper",
selected = selectedMode == SignatureMode.Manual,
onClick = { selectedMode = SignatureMode.Manual },
modifier = Modifier.fillMaxWidth()
.height(200.dp)
.border(width = 1.dp, color = AppColor.LegacyRed)
)
Spacer(Modifier.height(12.dp))
SignatureModeOption(
title = "Digital Sign",
subtitle = "Draw on pad",
selected = selectedMode == SignatureMode.Digital,
onClick = { selectedMode = SignatureMode.Digital },
modifier = Modifier.fillMaxWidth()
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
OutlinedButton(
onClick = { clearSignature() },
onClick = { signaturePadAdapter?.clear() },
modifier = Modifier
.weight(1f)
.height(50.dp)
@ -231,9 +92,10 @@ fun SignaturePad(
Button(
onClick = {
val bitmap = buildBitmap()
val rawBitmap = signaturePadAdapter?.getSignatureBitmap()
val bitmap = rawBitmap?.let { scaleBitmap(it) }
val signatureBlob = bitmap?.let { bitmapToBlob(it) }
onConfirm(selectedMode, bitmap, signatureBlob)
onConfirm(bitmap, signatureBlob)
},
modifier = Modifier
.weight(1f)

View File

@ -2,6 +2,7 @@ package com.mob.utsmyanmar.ui.tms_setup
import android.annotation.SuppressLint
import android.content.Context
import androidx.core.content.edit
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mob.utsmyanmar.model.sirius.SiriusResponse
@ -48,7 +49,7 @@ class TmsSetupViewModel @Inject constructor(
private val emvParamOperation: EmvParamOperation,
@ApplicationContext private val context: Context
) : ViewModel() {
private val TAG = TmsSetupViewModel::class.simpleName;
private val _uiState = MutableStateFlow(TmsSetupUiState())
private val _dashboardUiState = MutableStateFlow(DashboardUiState())
val uiState: StateFlow<TmsSetupUiState> = _uiState.asStateFlow()
@ -144,19 +145,27 @@ class TmsSetupViewModel @Inject constructor(
val ops = SystemParamsOperation.getInstance()
if (!ops.isActive) {
LogUtil.d(TAG, "isActive ${ops.isActive}")
val msg = ops.disabledMsg.takeIf { it.isNotEmpty() }
?: "This terminal has been disabled. Please contact your administrator."
_uiState.update { it.copy(isLoading = false) }
viewModelScope.launch { _navigateToDisable.emit(msg) }
_uiState.update {
it.copy(
isLoading = false,
isError = true,
isTerminalDisabled = true,
statusText = "Terminal Disabled",
errorMessage = msg
)
}
return
}
val validity = TMSUtil.getInstance().checkParams()
if (validity.status == ValidityStatus.SUCCESS) {
context.getSharedPreferences("mobpos_prefs", Context.MODE_PRIVATE)
.edit()
.putLong("last_sync_time", System.currentTimeMillis())
.apply()
.edit {
putLong("last_sync_time", System.currentTimeMillis())
}
_uiState.update { it.copy(isLoading = false, statusText = "Ready.") }
viewModelScope.launch {
_onSyncCompleted.emit(Unit)

View File

@ -24,6 +24,8 @@ 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.toFormattedDate
import com.mob.utsmyanmar.utils.toFormattedTime
import com.utsmyanmar.paylibs.model.PayDetail
import com.utsmyanmar.paylibs.print.PrintReceipt
import com.utsmyanmar.paylibs.utils.POSUtil
@ -82,8 +84,8 @@ fun TransactionResultScreen(
Spacer(modifier = Modifier.height(16.dp))
InfoCard(
date = "26 May 2026",
time = "12:06 PM",
date = "${state.payDetail?.transDate?.toFormattedDate()}",
time = "${state.payDetail?.transTime?.toFormattedTime()}",
transactionId = state.payDetail?.referNo.toString()
)

View File

@ -6,7 +6,6 @@ import com.mob.utsmyanmar.model.ecr.ECRResultStatus
import com.mob.utsmyanmar.utils.CoreUtils
import com.mob.utsmyanmar.viewmodel.SharedViewModel
import com.utsmyanmar.baselib.emv.EmvParamOperation
import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest
import com.utsmyanmar.ecr.ECRHelper
import com.utsmyanmar.ecr.ECRProcess
import com.utsmyanmar.ecr.data.model.TransactionsResp

View File

@ -0,0 +1,18 @@
package com.mob.utsmyanmar.utils
import java.text.SimpleDateFormat
import java.util.Locale
fun String.toFormattedDate(): String {
val inputFormat = SimpleDateFormat("dd/MM/yy", Locale.ENGLISH)
val outputFormat = SimpleDateFormat("dd MMM yyyy", Locale.ENGLISH)
val date = inputFormat.parse(this)
return outputFormat.format(date!!)
}
fun String.toFormattedTime(): String {
val inputFormat = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH)
val outputFormat = SimpleDateFormat("hh:mm a", Locale.ENGLISH)
val time = inputFormat.parse(this)
return outputFormat.format(time!!)
}

View File

@ -131,7 +131,7 @@ class TMSSetupsImpl : TMSSetups {
}
//Properties
for (prop in siriusProperty.orEmpty()) {
for (prop in siriusProperty) {
val name = prop.name
val data = prop.property
@ -221,6 +221,7 @@ class TMSSetupsImpl : TMSSetups {
"speedup_contactless_enable" -> ops.setSpeedUpContactless(parseBoolean(data))
"manual_entry_pin_enable" -> ops.isManualEntryPinEnable = parseBoolean(data)
"cmhl_enabled" -> ops.setCMHLEnable(parseBoolean(data))
"mmqr_enable" -> ops.setMmqrStatus(parseBoolean(data))
}
}
ops.carouselUrls = imgUrls.joinToString(",")

View File

@ -4,6 +4,7 @@ import android.os.Handler
import android.os.Looper
import android.os.Message
import android.text.TextUtils
import com.mob.utsmyanmar.model.TransResultStatus
import com.utsmyanmar.baselib.emv.EmvParamOperation
import com.utsmyanmar.baselib.repo.Repository
import com.utsmyanmar.baselib.util.enums.EmvResultStatus
@ -24,7 +25,6 @@ import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType
import dagger.hilt.android.lifecycle.HiltViewModel
import sunmi.sunmiui.utils.LogUtil
import javax.inject.Inject
import com.mob.utsmyanmar.model.TransResultStatus
@HiltViewModel
class EmvTransactionProcessViewModel @Inject constructor(

View File

@ -25,7 +25,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@HiltViewModel
class TransProcessViewModel @Inject constructor(

View File

@ -1,17 +1,12 @@
package com.utsmyanmar.ecr
import androidx.annotation.Keep
import com.google.gson.Gson
import com.utsmyanmar.ecr.data.ResultOf
import com.utsmyanmar.ecr.data.TransType
import com.utsmyanmar.ecr.data.mapper.RequestTransMapper
import com.utsmyanmar.ecr.data.mapper.ResponseTransMapper
import com.utsmyanmar.ecr.data.model.RequestTrans
import com.utsmyanmar.ecr.data.model.ResponseTrans
import com.utsmyanmar.ecr.data.model.Transactions
import com.utsmyanmar.ecr.data.model.TransactionsResp
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation
import java.lang.Exception
//@Keep
object ECRProcess {

View File

@ -503,7 +503,7 @@ public class ISOSocket {
LogUtil.e(Constant.TAG, "E:" + e.toString());
closeSocket();
// closeSocket();
if (isReversal) {
listener.onError(Constant.REVERSAL);
// listener.onError(new ReversalException());
@ -519,7 +519,7 @@ public class ISOSocket {
@Override
public void onComplete() {
LogUtil.d(Constant.TAG, "onComplete");
closeSocket();
// closeSocket();
isReversal = false;
listener.onComplete();
}

View File

@ -1189,10 +1189,11 @@ public class PrintReceipt {
private void printSignature(Bitmap signatureBitmap) {
try {
sunmiPrinterService.setAlignment(1, null);
if (signatureBitmap.getWidth() > 130) {
double size = 1.0 * signatureBitmap.getHeight() * 130 / signatureBitmap.getWidth();
int newHeight = (int) size;
signatureBitmap = BitmapUtils.scale(signatureBitmap, 130, newHeight);
int targetHeight = 200;
if (signatureBitmap.getHeight() > targetHeight) {
double ratio = 1.0 * targetHeight / signatureBitmap.getHeight();
int newWidth = (int) (signatureBitmap.getWidth() * ratio);
signatureBitmap = BitmapUtils.scale(signatureBitmap, newWidth, targetHeight);
}
sunmiPrinterService.printBitmap(signatureBitmap, null);
sunmiPrinterService.printText("\n", null);

View File

@ -954,6 +954,12 @@ public abstract class BaseXPrint {
if (signatureBlob != null && signatureBlob.length > 0) {
Bitmap signatureBitmap = android.graphics.BitmapFactory.decodeByteArray(signatureBlob, 0, signatureBlob.length);
if (signatureBitmap != null) {
int targetHeight = 200;
if (signatureBitmap.getHeight() > targetHeight) {
double ratio = 1.0 * targetHeight / signatureBitmap.getHeight();
int newWidth = (int) (signatureBitmap.getWidth() * ratio);
signatureBitmap = BitmapUtils.scale(signatureBitmap, newWidth, targetHeight);
}
sunmiPrinterService.printBitmap(signatureBitmap, innerResultCallback);
}
}

View File

@ -368,7 +368,7 @@ public class TransactionsOperation {
}
}
} else if (transactionsType == TransactionsType.PRE_AUTH_VOID) {
newPay.setCardType(oldPay.getCardType());
// newPay.setCardType(oldPay.getCardType());
newPay.setCustomOrderNo(oldPay.getVoucherNo());
newPay.setReferNo(oldPay.getReferNo());
newPay.setCardInfo(oldPay.getCardInfo());

View File

@ -1,16 +1,16 @@
package com.utsmyanmar.paylibs.utils.core_utils;
import static com.utsmyanmar.paylibs.utils.core_utils.SystemParamsSettings.TRANSACTION_MANAGER_KEY;
import android.text.TextUtils;
import com.utsmyanmar.paylibs.utils.enums.CurrencyType;
import java.util.ArrayList;
import java.util.Locale;
import sunmi.sunmiui.utils.LogUtil;
import static com.utsmyanmar.paylibs.utils.core_utils.SystemParamsSettings.TRANSACTION_MANAGER_KEY;
import com.utsmyanmar.paylibs.utils.enums.CurrencyType;
public class SystemParamsOperation {
public static final String TAG = "SystemParamsOperation";
@ -33,54 +33,6 @@ public class SystemParamsOperation {
return instance;
}
/* public void init(String serialNumStart,
String batchNumStart,
int purchaseOrderStyle,
int correctFrequency,
int transactionNumber,
boolean printNegative,
int nonConnection,
boolean stateSecret,
boolean shieldAuthorizationCard,
int nonConnectionChoice,
boolean eCashVisaExemption) {
SystemParamsSettings bean = getSystemParamsSettings();
bean.setSerialNum(serialNumStart);
bean.setBatchNumStart(batchNumStart);
bean.setPurchaseOrderStyle(purchaseOrderStyle);
bean.setCorrectFrequency(correctFrequency);
bean.setTransactionNumber(transactionNumber);
bean.setPrintNegative(printNegative);
bean.setNonConnection(nonConnection);
bean.setStateSecret(stateSecret);
bean.setShieldAuthorizationCard(shieldAuthorizationCard);
bean.setNonConnectionChoice(nonConnectionChoice);
bean.setCashVisaExemption(eCashVisaExemption);
saveSystemParamsSettings(bean);
}*/
/*public boolean isDownloadParamsOnce() {
SystemParamsSettings params = getSystemParamsSettings();
return params.isDownloadParamsOnce();
}
public void saveDownloadParamsOnce(boolean downloadParamsOnce) {
SystemParamsSettings params = getSystemParamsSettings();
params.setDownloadParamsOnce(downloadParamsOnce);
saveSystemParamsSettings(params);
}
public boolean isNeedDownLoadICParams() {
SystemParamsSettings params = getSystemParamsSettings();
return params.isNeedDownLoadICParams();
}
public void saveNeedDownLoadICParams(boolean needDownLoadICParams) {
SystemParamsSettings params = getSystemParamsSettings();
params.setNeedDownLoadICParams(needDownLoadICParams);
saveSystemParamsSettings(params);
}*/
public boolean isInjectOnce(){
SystemParamsSettings params = getSystemParamsSettings();
return params.isInjectOnce();
@ -91,13 +43,6 @@ public class SystemParamsOperation {
params.setInjectOnce(injectOnce);
saveSystemParamsSettings(params);
}
/**
* Set serial number
*
* @param serialNumber Starting serial number
*/
public void saveSerialNumber(String serialNumber) {
if (TextUtils.isEmpty(serialNumber)) {
return;
@ -106,12 +51,6 @@ public class SystemParamsOperation {
params.setSerialNum(serialNumber);
saveSystemParamsSettings(params);
}
/**
* Set invoice number
*
* @param invoiceNumber Starting invoice number
*/
public void saveInvoiceNumber(String invoiceNumber) {
if (TextUtils.isEmpty(invoiceNumber)) {
return;
@ -120,13 +59,6 @@ public class SystemParamsOperation {
params.setInvoiceNum(invoiceNumber);
saveSystemParamsSettings(params);
}
/**
* Set batch number
*
* @param batchNumber Starting batch number
*/
public void saveBatchNumber(String batchNumber) {
if (TextUtils.isEmpty(batchNumber)) {
return;
@ -136,11 +68,6 @@ public class SystemParamsOperation {
saveSystemParamsSettings(params);
}
/**
* Serial number increment
*/
private void incrementSerialNum() {
SystemParamsSettings params = getSystemParamsSettings();
String currentSerialNum = params.getCurrentSerialNum();
@ -172,10 +99,6 @@ public class SystemParamsOperation {
params.setSerialNum(transNumFormat);
saveSystemParamsSettings(params);
}
/**
* Invoice number increment
*/
private void incrementInvoiceNum() {
SystemParamsSettings params = getSystemParamsSettings();
String currentInvoiceNum = params.getCurrentInvoiceNum();
@ -952,6 +875,17 @@ public class SystemParamsOperation {
saveSystemParamsSettings(params);
}
public void setMmqrStatus(boolean status) {
SystemParamsSettings params = getSystemParamsSettings();
params.setMmqrEnabled(status);
saveSystemParamsSettings(params);
}
public boolean getMmqrStatus(){
SystemParamsSettings params = getSystemParamsSettings();
return params.getMmqrEnabled();
}
public boolean isCMHLEnabled(){
SystemParamsSettings params = getSystemParamsSettings();
return params.isCmhlEnabled();

View File

@ -233,6 +233,7 @@ public class SystemParamsSettings implements Serializable {
private byte[] pik_value;
private byte[] pik_kcv;
private boolean mmqrEnabled;
protected String getSecHostName() {
return secHostName;
@ -902,6 +903,14 @@ public class SystemParamsSettings implements Serializable {
this.emvEnabled = emvEnabled;
}
public void setMmqrEnabled(boolean status) {
this.mmqrEnabled = status;
}
public boolean getMmqrEnabled(){
return mmqrEnabled;
}
/* // 流水号起始
private String serialNum = Configs.getInstance().SERIAL_NUM();
// 批次号起始

View File

@ -117,7 +117,10 @@ public class HostUtils {
msgType = MessageType.AUTHORIZATION;
}
break;
case PRE_AUTH_VOID:
case PRE_AUTH_COMPLETE:
if (hostName == HostName.BPC) {
msgType = MessageType.FINANCIAL;
@ -142,11 +145,11 @@ public class HostUtils {
break;
case VOID:
case PRE_AUTH_VOID:
if(isVisaMaster) {
msgType = MessageType.REVERSAL;
break;
}
default:
msgType = MessageType.FINANCIAL;
break;

View File

@ -198,7 +198,8 @@ public class BitmapConfig {
public static final String CZ_REVERSAL="7020078000C08000";
public static final String CZ_BATCH_UPLOAD="703805800CC00010";
public static final String CZ_PRE_AUTH="7020058020C09000";
public static final String CZ_PRE_AUTH_VOID="7038058028C08000";
// public static final String CZ_PRE_AUTH_VOID="7038058028C08000";
public static final String CZ_PRE_AUTH_VOID="703C058008C08000";
public static final String CZ_PRE_AUTH_COMP="703805802CC08000";
public static final String CZ_PRE_AUTH_COMP_VOID="7038058008C00000";