Compare commits

..

8 Commits

Author SHA1 Message Date
moon
4884152faa receipt print animate re-scale 2026-07-20 21:24:08 +06:30
moon
beb6ddcba8 auto route to dashboard after print finished in sale 2026-07-20 21:18:03 +06:30
moon
b2e8e13a81 recent transaction rework 2026-07-20 20:53:03 +06:30
moon
511c3e0128 amount and card number in signature pad 2026-07-20 20:46:26 +06:30
moon
40c392cc01 void remove signature 2026-07-20 19:56:08 +06:30
moon
86f3e099db update config after every transaction 2026-07-20 19:40:49 +06:30
moon
f4d9dfaf7b Create opencode.json 2026-07-20 14:36:16 +06:30
moon
b3cb166f1a signature pad fix 2026-07-17 14:52:46 +06:30
13 changed files with 404 additions and 203 deletions

View File

@ -40,8 +40,8 @@ android {
dimension = "env"
applicationIdSuffix = ".uat"
versionNameSuffix = "-UAT"
versionCode = 12
versionName = "3.0"
versionCode = 14
versionName = "3.2"
buildConfigField("String", "ENV", "\"UAT\"")
resValue("string", "app_name", "MOB-UAT")
}

10
app/changelog.md Normal file
View File

@ -0,0 +1,10 @@
## Change Logs For UAT
### version 3.3
* config auto update after transaction
* signature removed on void
* "Enter Password" color to LegacyRed
* Amount and Card Number show in signature pad
* fixed recent transaction only working for top 3
* routed to dashboard after printing receipt
* receipt printing animation re-scale

View File

@ -25,24 +25,25 @@ 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> = buildList {
): List<DashboardMenuItem> {
val sp = SystemParamsOperation.getInstance()
val settlementEnabled = sp.settlementStatus
val voidEnabled = sp.voidStatus
val refundEnabled = sp.refundStatus
val preAuthEnabled = sp.preAuthStatus
val preAuthVoidEnabled = sp.preAuthStatus
val preAuthCompleteEnabled = sp.preAuthStatus
val preAuthCompleteVoidEnabled = sp.preAuthStatus
val cashAdvEnabled = sp.cashAdvanceStatus
val mmqrEnabled = sp.mmqrStatus
return buildList {
val mmqrEnabled = SystemParamsOperation.getInstance().mmqrStatus
add(
DashboardMenuItem(
@ -59,175 +60,174 @@ fun buildMenuItems(
)
if (mmqrEnabled) {
if (mmqrEnabled) {
add(
DashboardMenuItem(
"MMQR",
{
Image(
painter = painterResource(R.drawable.ic_mmqr_logo),
contentDescription = null,
modifier = Modifier.height(48.dp)
)
}
) { }
)
}
add(
DashboardMenuItem(
"MMQR",
{
Image(
painter = painterResource(R.drawable.ic_mmqr_logo),
contentDescription = null,
modifier = Modifier.height(48.dp)
)
}
) { }
)
}
add(
DashboardMenuItem(
"History",
{
Icon(
painterResource(R.drawable.ic_history),
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateHistory() }
)
add(
DashboardMenuItem(
"Sign On",
{
Icon(
painterResource(R.drawable.ic_sign_on),
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateSignOn() }
)
if (settlementEnabled) {
add(
DashboardMenuItem(
"Settlement",
"History",
{
Icon(
painterResource(R.drawable.ic_settlement),
painterResource(R.drawable.ic_history),
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateSettlement() }
) { onNavigateHistory() }
)
}
if (voidEnabled) {
add(
DashboardMenuItem(
"Void",
"Sign On",
{
Icon(
Icons.Default.Lock,
painterResource(R.drawable.ic_sign_on),
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateAction("Void") }
) { onNavigateSignOn() }
)
if (settlementEnabled) {
add(
DashboardMenuItem(
"Settlement",
{
Icon(
painterResource(R.drawable.ic_settlement),
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateSettlement() }
)
}
if (voidEnabled) {
add(
DashboardMenuItem(
"Void",
{
Icon(
Icons.Default.Lock,
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateAction("Void") }
)
}
if (refundEnabled) {
add(
DashboardMenuItem(
"Refund",
{
Icon(
Icons.Default.Replay,
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateAction("Refund") }
)
}
if (preAuthEnabled) {
add(
DashboardMenuItem(
"Pre-Auth",
{
Icon(
Icons.Default.Lock,
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateAmount("Pre-Auth") }
)
}
if (preAuthVoidEnabled) {
add(
DashboardMenuItem(
"Pre-Auth Void",
{
Icon(
Icons.Default.LockOpen,
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateAction("Pre-Auth Void") }
)
}
if (preAuthCompleteEnabled) {
add(
DashboardMenuItem(
"Pre-Auth Complete",
{
Icon(
Icons.Default.CreditCard,
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateAction("Pre-Auth Complete") }
)
}
if (preAuthCompleteVoidEnabled) {
add(
DashboardMenuItem(
"Pre-Auth Comp Void",
{
Icon(
Icons.Default.SwapHoriz,
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateAction("Pre-Auth Complete Void") }
)
}
if (cashAdvEnabled) {
add(
DashboardMenuItem(
"Cash Adv",
{
Icon(
Icons.Default.AccountBalanceWallet,
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateAmount("Cash Adv") }
)
}
}
if (refundEnabled) {
add(
DashboardMenuItem(
"Refund",
{
Icon(
Icons.Default.Replay,
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateAction("Refund") }
)
}
if (preAuthEnabled) {
add(
DashboardMenuItem(
"Pre-Auth",
{
Icon(
Icons.Default.Lock,
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateAmount("Pre-Auth") }
)
}
if (preAuthVoidEnabled) {
add(
DashboardMenuItem(
"Pre-Auth Void",
{
Icon(
Icons.Default.LockOpen,
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateAction("Pre-Auth Void") }
)
}
if (preAuthCompleteEnabled) {
add(
DashboardMenuItem(
"Pre-Auth Complete",
{
Icon(
Icons.Default.CreditCard,
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateAction("Pre-Auth Complete") }
)
}
if (preAuthCompleteVoidEnabled) {
add(
DashboardMenuItem(
"Pre-Auth Comp Void",
{
Icon(
Icons.Default.SwapHoriz,
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateAction("Pre-Auth Complete Void") }
)
}
if (cashAdvEnabled) {
add(
DashboardMenuItem(
"Cash Adv",
{
Icon(
Icons.Default.AccountBalanceWallet,
contentDescription = null,
modifier = Modifier.size(32.dp),
tint = Color.LegacyRed
)
}
) { onNavigateAmount("Cash Adv") }
)
}
}

View File

@ -92,6 +92,8 @@ fun AppNavGraph(
val sharedViewModel: SharedViewModel = hiltViewModel(activity)
SignaturePad(
onBack = { navController.popBackStack() },
amount = sharedViewModel.payDetail.value?.amount,
cardNumber = sharedViewModel.payDetail.value?.cardNo ?: "",
onConfirm = { bitmap, signatureBlob ->
sharedViewModel.signBitmap = bitmap
sharedViewModel.updateSignatureBlob(signatureBlob)
@ -111,10 +113,10 @@ fun AppNavGraph(
val transResultViewModel: TransactionResultViewModel = hiltViewModel(activity)
val dashboardViewModel: DashboardViewModel = hiltViewModel(activity)
val dashboardUiState = dashboardViewModel.uiState.collectAsStateWithLifecycle().value
val recentPayDetails by sharedViewModel.getLastThreeTransactions().observeAsState(emptyList())
val recentPayDetails by sharedViewModel.getLastTenTransactions().observeAsState(emptyList())
val deviceInfoViewModel: DeviceInfoViewModel = hiltViewModel(activity)
val deviceInfoUiState = deviceInfoViewModel.uiState.collectAsStateWithLifecycle().value
val tmsSetupViewModel: TmsSetupViewModel = hiltViewModel();
val tmsSetupViewModel: TmsSetupViewModel = hiltViewModel(activity);
LaunchedEffect(deviceInfoViewModel) {
deviceInfoViewModel.loadDeviceInfo()
@ -750,9 +752,14 @@ fun AppNavGraph(
composable(Routes.TransactionResult.route) {
val sharedViewModel: SharedViewModel = hiltViewModel(activity)
val transResultViewModel: TransactionResultViewModel = hiltViewModel(activity)
val tmsSetupViewModel: TmsSetupViewModel = hiltViewModel(activity)
val dashboardViewModel: DashboardViewModel = hiltViewModel(activity)
TransactionResultRoute(
viewModel = transResultViewModel,
sharedViewModel = sharedViewModel,
tmsSetupViewModel = tmsSetupViewModel,
dashboardViewModel = dashboardViewModel,
onNavigateMain = {
navController.navigate(Routes.Dashboard.route) {
popUpTo(Routes.Dashboard.route) {
@ -794,6 +801,14 @@ fun AppNavGraph(
}
launchSingleTop = true
}
},
onPrintFinished = {
navController.navigate(Routes.Dashboard.route) {
popUpTo(Routes.Dashboard.route) {
inclusive = false
}
launchSingleTop = true
}
}
)
}

View File

@ -112,7 +112,7 @@ fun InputPassword(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Enter Password", color = Color.Gray, fontSize = 18.sp)
Text(text = "Enter Password", color = Color.LegacyRed, fontSize = 18.sp)
Spacer(Modifier.height(16.dp))
PasswordDots(filledCount = uiState.input.length, totalCount = PASSWORD_LENGTH)
Spacer(Modifier.height(8.dp))

View File

@ -44,9 +44,7 @@ import com.mob.utsmyanmar.ui.transaction_result.TransactionResultViewModel
import com.mob.utsmyanmar.viewmodel.SharedViewModel
import com.utsmyanmar.paylibs.utils.POSUtil
import com.utsmyanmar.paylibs.utils.PrintStatus
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.milliseconds
private enum class ReceiptCopy(val label: String) {
MERCHANT("MERCHANT COPY"),
@ -58,12 +56,12 @@ fun PrintReceiptScreen(
transactionResultViewModel: TransactionResultViewModel,
sharedViewModel: SharedViewModel,
onPrint: () -> Unit,
onDone: () -> Unit
onDone: () -> Unit,
onPrintFinished : () -> Unit,
) {
val state by transactionResultViewModel.state.collectAsStateWithLifecycle()
val scope = rememberCoroutineScope()
val receiptOffsetY = remember { Animatable(0f) }
val animationDuration = 5000
val merchant_copy_autoprint = true
val customer_copy_autoprint = false
var currentCopy by remember { mutableStateOf(ReceiptCopy.MERCHANT) }
@ -72,10 +70,10 @@ fun PrintReceiptScreen(
suspend fun animatePrint() {
receiptOffsetY.animateTo(
targetValue = -1500f,
animationSpec = tween(durationMillis = animationDuration, easing = FastOutSlowInEasing)
targetValue = -1400f,
animationSpec = tween(durationMillis = 4000, easing = FastOutSlowInEasing)
)
delay(1000.milliseconds)
// delay(1000.milliseconds)
receiptOffsetY.animateTo(
targetValue = 0f,
animationSpec = tween(durationMillis = 0, easing = FastOutSlowInEasing)
@ -84,21 +82,30 @@ fun PrintReceiptScreen(
fun isReprintMode() = sharedViewModel.isReprint.value == true
fun doPrint(copy: ReceiptCopy, onFinished: () -> Unit) {
fun doPrint(copy: ReceiptCopy, onFinished: (printFinished: ReceiptCopy) -> Unit) {
val reprint = isReprintMode()
val actualCopy = if (reprint && copy == ReceiptCopy.MERCHANT) ReceiptCopy.CUSTOMER else copy
currentCopy = actualCopy
sharedViewModel.setPrintStatus(
if (actualCopy == ReceiptCopy.MERCHANT) PrintStatus.FIRST_PRINT else PrintStatus.SECOND_PRINT
if (actualCopy == ReceiptCopy.MERCHANT)
PrintStatus.FIRST_PRINT
else
PrintStatus.SECOND_PRINT
)
onPrint()
scope.launch {
isPrinting = true
animatePrint()
isPrinting = false
nextCopy = if (reprint) ReceiptCopy.CUSTOMER
else if (actualCopy == ReceiptCopy.MERCHANT) ReceiptCopy.CUSTOMER else ReceiptCopy.MERCHANT
onFinished()
val upcomingCopy = if (reprint) ReceiptCopy.CUSTOMER
else if (actualCopy == ReceiptCopy.MERCHANT) ReceiptCopy.CUSTOMER
else ReceiptCopy.CUSTOMER
currentCopy = upcomingCopy
nextCopy = upcomingCopy
onFinished(actualCopy)
}
}
@ -174,7 +181,12 @@ fun PrintReceiptScreen(
),
shape = RoundedCornerShape(12.dp),
onClick = {
doPrint(nextCopy) { }
doPrint(nextCopy) { printFinished ->
if (printFinished == ReceiptCopy.CUSTOMER) {
onPrintFinished()
}
}
}
) {
Text(
@ -295,7 +307,8 @@ fun PreviewPrintReceiptScreen() {
sharedViewModel = hiltViewModel(),
transactionResultViewModel = hiltViewModel(),
onPrint = {},
onDone = {}
onDone = {},
onPrintFinished = {}
)
}
}

View File

@ -55,7 +55,7 @@ fun ProcessingRoute(
when(transStatus) {
TransResultStatus.SUCCESS -> {
sharedViewModel.payDetail.value = transProcessViewModel.payDetailResult.value
if (requiresSignature(sharedViewModel.payDetail.value)) {
if (requiresSignature(sharedViewModel.transactionsType.value, sharedViewModel.payDetail.value)) {
onNavigateSignature()
} else {
onNavigateTransactionResult()
@ -94,7 +94,9 @@ fun ProcessingRoute(
}
private fun requiresSignature(payDetail: com.utsmyanmar.paylibs.model.PayDetail?): Boolean {
// return payDetail?.transCVM == TransCVM.SIGNATURE && payDetail.isFreeSign == false
return true;
private fun requiresSignature(transType: TransactionsType?, payDetail: com.utsmyanmar.paylibs.model.PayDetail?): Boolean {
if (transType == TransactionsType.VOID || transType == TransactionsType.PRE_AUTH_COMPLETE_VOID || transType == TransactionsType.PRE_AUTH_VOID) {
return false
}
return true
}

View File

@ -1,6 +1,7 @@
package com.mob.utsmyanmar.ui.signature_pad
import android.graphics.Bitmap
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@ -15,16 +16,21 @@ import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
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.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.mob.utsmyanmar.ui.components.appbar.AppBar
import com.mob.utsmyanmar.ui.theme.MOBPOSTheme
import com.mob.utsmyanmar.utils.formatAmount
import com.mob.utsmyanmar.utils.maskCardNumber
import se.warting.signaturepad.SignaturePadAdapter
import se.warting.signaturepad.SignaturePadView
import java.io.ByteArrayOutputStream
@ -34,9 +40,13 @@ import com.mob.utsmyanmar.ui.theme.Color as AppColor
fun SignaturePad(
modifier: Modifier = Modifier,
onBack: () -> Unit = {},
onConfirm: (Bitmap?, ByteArray?) -> Unit = { _, _ -> }
onConfirm: (Bitmap?, ByteArray?) -> Unit = { _, _ -> },
amount: Long? = 0L,
cardNumber: String,
) {
var signaturePadAdapter: SignaturePadAdapter? = null
var selectedMode by remember { mutableStateOf(SignatureMode.Digital) }
val isManualMode = selectedMode == SignatureMode.Manual
val scaledWidth = 300
@ -69,33 +79,137 @@ fun SignaturePad(
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
SignaturePadView(
onReady = { signaturePadAdapter = it },
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.border(width = 1.dp, color = AppColor.LegacyRed)
)
Column(
modifier = Modifier.padding(0.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = "Amount",
style = MaterialTheme.typography.titleSmall,
color = AppColor.LegacyRed
)
Text(
text = amount.formatAmount(),
style = MaterialTheme.typography.bodySmall,
color = AppColor.LegacyRed
)
}
Column(
modifier = Modifier.padding(0.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = "Card Number",
style = MaterialTheme.typography.titleSmall,
color = AppColor.LegacyRed
)
Text(
text = cardNumber.maskCardNumber(),
style = MaterialTheme.typography.bodySmall,
color = AppColor.LegacyRed
)
}
if (isManualMode) {
Card(
modifier = Modifier
.fillMaxWidth()
.height(200.dp),
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
border = androidx.compose.foundation.BorderStroke(
width = 1.dp,
color = AppColor.Gray.copy(alpha = 0.35f)
)
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = androidx.compose.ui.Alignment.CenterHorizontally
) {
Text(
text = "Manual Signature",
style = MaterialTheme.typography.titleMedium,
color = AppColor.Gray
)
Text(
text = "Customer need to sign on paper",
style = MaterialTheme.typography.bodySmall,
color = AppColor.Gray.copy(alpha = 0.7f)
)
}
}
} else {
SignaturePadView(
onReady = { signaturePadAdapter = it },
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.background(AppColor.White)
.border(
width = 1.dp,
color = AppColor.LegacyRed,
shape = RoundedCornerShape(8.dp)
),
)
}
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
SignatureModeOption(
title = "Digital",
subtitle = "Sign on screen",
selected = !isManualMode,
onClick = { selectedMode = SignatureMode.Digital },
modifier = Modifier.fillMaxWidth()
)
SignatureModeOption(
title = "Manual",
subtitle = "Sign on paper",
selected = isManualMode,
onClick = { selectedMode = SignatureMode.Manual },
modifier = Modifier.fillMaxWidth()
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
OutlinedButton(
onClick = { signaturePadAdapter?.clear() },
Button(
onClick = {
if (!isManualMode) {
signaturePadAdapter?.clear()
}
},
colors = ButtonDefaults.buttonColors(
containerColor = AppColor.White,
contentColor = Color.White
),
modifier = Modifier
.weight(1f)
.height(50.dp)
.height(50.dp),
enabled = !isManualMode
) {
Text("Clear")
Text("Clear", color = AppColor.Black)
}
Button(
onClick = {
val rawBitmap = signaturePadAdapter?.getSignatureBitmap()
val bitmap = rawBitmap?.let { scaleBitmap(it) }
val signatureBlob = bitmap?.let { bitmapToBlob(it) }
onConfirm(bitmap, signatureBlob)
if (isManualMode) {
onConfirm(null, null)
} else {
val rawBitmap = signaturePadAdapter?.getSignatureBitmap()
val bitmap = rawBitmap?.let { scaleBitmap(it) }
val signatureBlob = bitmap?.let { bitmapToBlob(it) }
onConfirm(bitmap, signatureBlob)
}
},
modifier = Modifier
.weight(1f)
@ -159,6 +273,9 @@ private fun SignatureModeOption(
@Composable
fun SignaturePadPreview() {
MOBPOSTheme {
SignaturePad()
SignaturePad(
amount = 1000000L,
cardNumber = "6244310034582136"
)
}
}

View File

@ -0,0 +1,4 @@
package com.mob.utsmyanmar.ui.transaction_result
class TransactionResultRepository {
}

View File

@ -4,8 +4,9 @@ package com.mob.utsmyanmar.ui.transaction_result
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.mob.utsmyanmar.ui.dashboard.DashboardViewModel
import com.mob.utsmyanmar.ui.tms_setup.TmsSetupViewModel
import com.mob.utsmyanmar.viewmodel.SharedViewModel
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType
@ -17,7 +18,9 @@ fun TransactionResultRoute(
onNavigatePrintReceipt: () -> Unit,
onShowError: (String) -> Unit,
onShowSuccess: (String) -> Unit,
onShowPrinterDialog: (String) -> Unit
onShowPrinterDialog: (String) -> Unit,
tmsSetupViewModel: TmsSetupViewModel,
dashboardViewModel: DashboardViewModel,
) {
val state by viewModel.state.collectAsStateWithLifecycle()
val canGoBack = sharedViewModel.transactionsType.value != TransactionsType.SALE
@ -27,6 +30,8 @@ fun TransactionResultRoute(
TransactionResultEvent.Start,
sharedViewModel
)
dashboardViewModel.refreshLastSyncTime()
tmsSetupViewModel.downloadConfigs()
}
LaunchedEffect(Unit) {

View File

@ -0,0 +1,20 @@
package com.mob.utsmyanmar.utils
import android.annotation.SuppressLint
@SuppressLint("DefaultLocale")
fun Long?.formatAmount(): String {
val amount = (this ?: 0L) / 100.0
return String.format("%,.2f", amount)
}
fun String?.maskCardNumber(): String {
if (this.isNullOrEmpty()) return ""
val maskedPan = StringBuilder(this)
if (this.length > 12) {
maskedPan.replace(6, 12, "******")
} else {
maskedPan.append(" ")
}
return maskedPan.toString()
}

View File

@ -438,6 +438,10 @@ class SharedViewModel @Inject constructor(
return repository.getLastThreeTransactions()
}
fun getLastTenTransactions(): LiveData<List<PayDetail>> {
return repository.getLastTenTransactions()
}
fun getSettlementRecords(): LiveData<List<PayDetail>> {
return repository.getSettlementPOS()
}

11
opencode.json Normal file
View File

@ -0,0 +1,11 @@
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"codebase-memory": {
"type": "local",
"command": "codebase-memory-mcp",
"args": [],
"enabled": true
}
}
}