Compare commits

..

2 Commits

Author SHA1 Message Date
moon
af9a5487b1 Update AppNavGraph.kt 2026-06-29 01:10:19 +06:30
moon
46ef4521cc show total sales 2026-06-29 01:03:15 +06:30
7 changed files with 98 additions and 36 deletions

View File

@ -449,7 +449,9 @@ fun DashboardScreen2(
Spacer(Modifier.height(12.dp)) Spacer(Modifier.height(12.dp))
SummaryCard( SummaryCard(
onClickLastSync = onClickLastSync, onClickLastSync = onClickLastSync,
lastSyncTime = dashboardUiState.lastSyncTime lastSyncTime = dashboardUiState.lastSyncTime,
totalSaleCount = dashboardUiState.totalSaleCount,
totalSaleAmount = dashboardUiState.totalSaleAmount
) )
Spacer(Modifier.height(12.dp)) Spacer(Modifier.height(12.dp))
} }
@ -527,7 +529,9 @@ private fun formatRelativeTime(timestamp: Long?, now: Long): String {
@Composable @Composable
private fun SummaryCard( private fun SummaryCard(
onClickLastSync: () -> Unit, onClickLastSync: () -> Unit,
lastSyncTime: Long? lastSyncTime: Long?,
totalSaleCount: Int,
totalSaleAmount: Long
) { ) {
var now by remember { mutableLongStateOf(System.currentTimeMillis()) } var now by remember { mutableLongStateOf(System.currentTimeMillis()) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
@ -554,9 +558,9 @@ private fun SummaryCard(
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
SummaryItem( SummaryItem(
title = "Sales Today", title = "Total Sale",
value = "MMK 2,000,000", value = "MMK ${formatAmount(totalSaleAmount)}",
subtitle = "47 Transactions", subtitle = "$totalSaleCount Transactions",
icon = Icons.Default.BarChart, icon = Icons.Default.BarChart,
iconBg = Color.CrimsonRed iconBg = Color.CrimsonRed
) )

View File

@ -13,5 +13,7 @@ data class TrnxRecord(
data class DashboardUiState( data class DashboardUiState(
val recentTransactions: List<TrnxRecord> = emptyList(), val recentTransactions: List<TrnxRecord> = emptyList(),
val lastSyncTime: Long? = null, val lastSyncTime: Long? = null,
val carouselUrls: List<String> = emptyList() val carouselUrls: List<String> = emptyList(),
val totalSaleCount: Int = 0,
val totalSaleAmount: Long = 0L
) )

View File

@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope
import com.utsmyanmar.baselib.repo.Repository import com.utsmyanmar.baselib.repo.Repository
import com.utsmyanmar.paylibs.model.PayDetail import com.utsmyanmar.paylibs.model.PayDetail
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -32,7 +33,9 @@ class DashboardViewModel @Inject constructor(
repository.getLastTenTransactions().asFlow().collect { payDetails -> repository.getLastTenTransactions().asFlow().collect { payDetails ->
_uiState.update { _uiState.update {
it.copy( it.copy(
recentTransactions = payDetails.map { pd -> pd.toRecord() } recentTransactions = payDetails.map { pd -> pd.toRecord() },
totalSaleCount = payDetails.totalSaleCount(),
totalSaleAmount = payDetails.totalSaleAmount()
) )
} }
} }
@ -58,8 +61,9 @@ class DashboardViewModel @Inject constructor(
} }
private fun PayDetail.toRecord(): TrnxRecord { private fun PayDetail.toRecord(): TrnxRecord {
val type = transType?.takeIf { it.isNotBlank() }?.let { formatTypeLabel(it) } ?: "Transaction" val baseType = transType?.takeIf { it.isNotBlank() }?.let { formatTypeLabel(it) } ?: "Transaction"
val amount = "MMK ${formatAmount(amount)}" val type = if (isCanceled) "Void" else baseType
val amountText = "MMK ${formatAmount(amount)}"
val card = CardNo?.takeIf { it.length >= 4 }?.let { "**** ${it.takeLast(4)}" } ?: "----" val card = CardNo?.takeIf { it.length >= 4 }?.let { "**** ${it.takeLast(4)}" } ?: "----"
val dt = buildDateTime(TradeDate, TradeTime) val dt = buildDateTime(TradeDate, TradeTime)
val approved = tradeAnswerCode == "00" || approvalCode?.isNotBlank() == true val approved = tradeAnswerCode == "00" || approvalCode?.isNotBlank() == true
@ -67,7 +71,7 @@ private fun PayDetail.toRecord(): TrnxRecord {
return TrnxRecord( return TrnxRecord(
pid = PID ?: 0L, pid = PID ?: 0L,
typeLabel = type, typeLabel = type,
amountDisplay= amount, amountDisplay = amountText,
maskedCard = card, maskedCard = card,
dateTime = dt, dateTime = dt,
isVoided = isCanceled, isVoided = isCanceled,
@ -75,11 +79,22 @@ private fun PayDetail.toRecord(): TrnxRecord {
) )
} }
fun formatAmount(amount: Long): String { fun formatAmount(amount: Long): String {
val scaled = BigDecimal(amount).movePointLeft(2) val scaled = BigDecimal(amount).movePointLeft(2)
return DecimalFormat("#,###.00").format(scaled) return DecimalFormat("#,###.00").format(scaled)
} }
private fun List<PayDetail>.totalSaleCount(): Int = count { it.isSaleTransaction() }
private fun List<PayDetail>.totalSaleAmount(): Long = sumOf {
if (it.isSaleTransaction()) it.amount else 0L
}
private fun PayDetail.isSaleTransaction(): Boolean {
return transactionType == TransactionsType.SALE.value ||
(transactionType == TransactionsType.WAVEPAY.value && qrTransStatus == 1)
}
private fun formatTypeLabel(raw: String): String = when { private fun formatTypeLabel(raw: String): String = when {
raw.contains("SALE", ignoreCase = true) -> "Sale" raw.contains("SALE", ignoreCase = true) -> "Sale"
raw.contains("VOID", ignoreCase = true) -> "Void" raw.contains("VOID", ignoreCase = true) -> "Void"

View File

@ -1,8 +1,11 @@
package com.mob.utsmyanmar.ui.navigation package com.mob.utsmyanmar.ui.navigation
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Build
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.annotation.RequiresApi
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
@ -50,6 +53,7 @@ import com.mob.utsmyanmar.viewmodel.TransProcessViewModel
import com.utsmyanmar.paylibs.model.TradeData import com.utsmyanmar.paylibs.model.TradeData
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType
@RequiresApi(Build.VERSION_CODES.N)
@SuppressLint("ContextCastToActivity") @SuppressLint("ContextCastToActivity")
@Composable @Composable
fun AppNavGraph( fun AppNavGraph(
@ -82,10 +86,27 @@ fun AppNavGraph(
val transProcessViewModel: TransProcessViewModel = hiltViewModel(activity) val transProcessViewModel: TransProcessViewModel = hiltViewModel(activity)
val dashboardViewModel: DashboardViewModel = hiltViewModel(activity) val dashboardViewModel: DashboardViewModel = hiltViewModel(activity)
val dashboardUiState = dashboardViewModel.uiState.collectAsStateWithLifecycle().value val dashboardUiState = dashboardViewModel.uiState.collectAsStateWithLifecycle().value
val deviceInfoViewModel: DeviceInfoViewModel = hiltViewModel(activity)
val deviceInfoUiState = deviceInfoViewModel.uiState.collectAsStateWithLifecycle().value
val tmsSetupViewModel: TmsSetupViewModel = hiltViewModel();
LaunchedEffect(deviceInfoViewModel) {
deviceInfoViewModel.loadDeviceInfo()
}
LaunchedEffect(tmsSetupViewModel) {
tmsSetupViewModel.onSyncCompleted.collect {
dashboardViewModel.refreshLastSyncTime()
}
}
transProcessViewModel.resetTransactionStatus() transProcessViewModel.resetTransactionStatus()
DashboardScreen2( DashboardScreen2(
dashboardUiState = dashboardUiState, dashboardUiState = dashboardUiState,
deviceInfo = deviceInfoUiState,
onClickLastSync = {
tmsSetupViewModel.downloadConfigs()
},
onNavigateAmount = { action -> onNavigateAmount = { action ->
when (action) { when (action) {
"Sale" -> { "Sale" -> {

View File

@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope
import com.mob.utsmyanmar.model.sirius.SiriusResponse import com.mob.utsmyanmar.model.sirius.SiriusResponse
import com.mob.utsmyanmar.model.sirius.TMSUpdate import com.mob.utsmyanmar.model.sirius.TMSUpdate
import com.mob.utsmyanmar.model.sirius.ValidityStatus import com.mob.utsmyanmar.model.sirius.ValidityStatus
import com.mob.utsmyanmar.ui.dashboard.DashboardUiState
import com.mob.utsmyanmar.utils.tms.TMSSetupsImpl import com.mob.utsmyanmar.utils.tms.TMSSetupsImpl
import com.mob.utsmyanmar.utils.tms.TMSUtil import com.mob.utsmyanmar.utils.tms.TMSUtil
import com.utsmyanmar.baselib.BaseApplication import com.utsmyanmar.baselib.BaseApplication
@ -49,6 +50,7 @@ class TmsSetupViewModel @Inject constructor(
) : ViewModel() { ) : ViewModel() {
private val _uiState = MutableStateFlow(TmsSetupUiState()) private val _uiState = MutableStateFlow(TmsSetupUiState())
private val _dashboardUiState = MutableStateFlow(DashboardUiState())
val uiState: StateFlow<TmsSetupUiState> = _uiState.asStateFlow() val uiState: StateFlow<TmsSetupUiState> = _uiState.asStateFlow()
private val _navigateToDashboard = MutableSharedFlow<Unit>() private val _navigateToDashboard = MutableSharedFlow<Unit>()
@ -76,14 +78,15 @@ class TmsSetupViewModel @Inject constructor(
it.copy(isLoading = true, isError = false, statusText = "Connecting to TMS server...") it.copy(isLoading = true, isError = false, statusText = "Connecting to TMS server...")
} }
val disposable = repository.getParams(buildRequest()) val disposable = repository.getParams(buildRequest())
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(
{ response -> { response ->
_uiState.update { it.copy(statusText = "Applying configuration...") } _uiState.update { it.copy(
statusText = "Applying configuration...")
}
val appResponse = SiriusResponse( val appResponse = SiriusResponse(
serial = response.serial.orEmpty(), serial = response.serial.orEmpty(),

View File

@ -1,5 +1,6 @@
package com.mob.utsmyanmar.viewmodel package com.mob.utsmyanmar.viewmodel
import android.content.Context
import android.text.TextUtils import android.text.TextUtils
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.mob.utsmyanmar.model.TransResultStatus import com.mob.utsmyanmar.model.TransResultStatus
@ -17,13 +18,15 @@ import com.utsmyanmar.paylibs.utils.PrintStatus
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class TransProcessViewModel @Inject constructor( class TransProcessViewModel @Inject constructor(
private val repository: Repository private val repository: Repository,
@ApplicationContext private val context: Context
) : ViewModel(), ProcessingTransaction { ) : ViewModel(), ProcessingTransaction {
companion object { companion object {
@ -184,6 +187,8 @@ class TransProcessViewModel @Inject constructor(
_payDetailResult.value = _payDetailResult.value =
payDetailRes payDetailRes
stampLastSyncTime()
_transResultStatus.value = _transResultStatus.value =
TransResultStatus.SUCCESS TransResultStatus.SUCCESS
@ -252,6 +257,7 @@ class TransProcessViewModel @Inject constructor(
repository.insertPayDetail( repository.insertPayDetail(
updateCurrentDateAndTime(payResult) updateCurrentDateAndTime(payResult)
) )
stampLastSyncTime()
} }
override fun processRefundDB(payResult: PayDetail) { override fun processRefundDB(payResult: PayDetail) {
@ -266,6 +272,7 @@ class TransProcessViewModel @Inject constructor(
repository.insertPayDetail( repository.insertPayDetail(
updateCurrentDateAndTime(payResult) updateCurrentDateAndTime(payResult)
) )
stampLastSyncTime()
} }
override fun processPreVoidDb(payResult: PayDetail) { override fun processPreVoidDb(payResult: PayDetail) {
@ -276,6 +283,7 @@ class TransProcessViewModel @Inject constructor(
repository.insertPayDetail(updateCurrentDateAndTime(payResult)) repository.insertPayDetail(updateCurrentDateAndTime(payResult))
stampLastSyncTime()
} }
override fun processPreCompDb(payResult: PayDetail) { override fun processPreCompDb(payResult: PayDetail) {
if (oldTransPayDetail?.getAmount() == payResult.getAmount()) { if (oldTransPayDetail?.getAmount() == payResult.getAmount()) {
@ -290,6 +298,7 @@ class TransProcessViewModel @Inject constructor(
} }
} }
repository.insertPayDetail(updateCurrentDateAndTime(payResult)) repository.insertPayDetail(updateCurrentDateAndTime(payResult))
stampLastSyncTime()
} }
override fun processPreCompVoidDb(payResult: PayDetail) { override fun processPreCompVoidDb(payResult: PayDetail) {
payDetail?.apply { payDetail?.apply {
@ -297,6 +306,7 @@ class TransProcessViewModel @Inject constructor(
repository.updatePayDetail(this) repository.updatePayDetail(this)
} }
repository.insertPayDetail(updateCurrentDateAndTime(payResult)) repository.insertPayDetail(updateCurrentDateAndTime(payResult))
stampLastSyncTime()
} }
/* /*
@ -371,6 +381,13 @@ class TransProcessViewModel @Inject constructor(
}) })
} }
private fun stampLastSyncTime() {
context.getSharedPreferences("mobpos_prefs", Context.MODE_PRIVATE)
.edit()
.putLong("last_sync_time", System.currentTimeMillis())
.apply()
}
/* /*
* Utils * Utils
*/ */