From 8d09e760a8c36da61801bdead911c978a6e7e39b Mon Sep 17 00:00:00 2001 From: ahusan Date: Thu, 28 May 2026 15:24:49 +0500 Subject: [PATCH] Enhance dashboard: add attention row for blocked and overdue funds Introduces a new attention row in the dashboard to display blocked funds and overdue financing. The row is conditionally visible based on the presence of blocked amounts or overdue totals. Updates the account display logic to show blocked amounts where applicable, ensuring users have a clear view of their financial status. Additionally, new string resources for "Blocked Funds" and "Overdue Financing" are added for localization. --- .../sh/sar/basedbank/api/mib/MibLoginFlow.kt | 12 ++- .../basedbank/ui/home/DashboardFragment.kt | 70 ++++++++++++- .../basedbank/util/mibapi/MibAccountParser.kt | 16 +-- .../basedbank/util/mibapi/MibHistoryParser.kt | 21 ++-- .../main/res/layout/fragment_dashboard.xml | 97 +++++++++++++++++++ app/src/main/res/layout/item_account.xml | 2 +- app/src/main/res/values/strings.xml | 2 + 7 files changed, 199 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/sh/sar/basedbank/api/mib/MibLoginFlow.kt b/app/src/main/java/sh/sar/basedbank/api/mib/MibLoginFlow.kt index 5a14071..4979253 100644 --- a/app/src/main/java/sh/sar/basedbank/api/mib/MibLoginFlow.kt +++ b/app/src/main/java/sh/sar/basedbank/api/mib/MibLoginFlow.kt @@ -9,6 +9,7 @@ import org.json.JSONObject import java.security.MessageDigest import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean +import kotlin.math.abs import kotlin.random.Random class SessionExpiredException : Exception("MIB session expired") @@ -168,7 +169,7 @@ class MibLoginFlow(private val credentialStore: CredentialStore) { accountTypeName = a.optString("accountTypeName"), availableBalance = a.optString("availableBalance"), currentBalance = a.optString("currentBalance"), - blockedAmount = a.optString("blockedAmount"), + blockedAmount = absBlockedAmount(a.optString("blockedAmount")), mvrBalance = a.optString("mvrBalance"), statusDesc = a.optString("statusDesc"), profileImageHash = profile.customerImage, @@ -188,6 +189,13 @@ class MibLoginFlow(private val credentialStore: CredentialStore) { // ─── Helpers ───────────────────────────────────────────────────────────── + /** MIB returns blockedAmount as a signed decimal where negative = funds held. + * Normalize to a positive magnitude so downstream code can treat it uniformly. */ + private fun absBlockedAmount(raw: String): String { + val v = raw.toDoubleOrNull() ?: return raw + return "%.2f".format(abs(v)) + } + private fun initialKeyExchange( appId: String, encKey: String, sfunc: String, key2: String? = null ): Pair { @@ -325,7 +333,7 @@ class MibLoginFlow(private val credentialStore: CredentialStore) { accountTypeName = a.optString("accountTypeName"), availableBalance = a.optString("availableBalance"), currentBalance = a.optString("currentBalance"), - blockedAmount = a.optString("blockedAmount"), + blockedAmount = absBlockedAmount(a.optString("blockedAmount")), mvrBalance = a.optString("mvrBalance"), statusDesc = a.optString("statusDesc"), profileImageHash = profile.customerImage, diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt index 8fbef5f..cfb38cd 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt @@ -57,14 +57,24 @@ class DashboardFragment : Fragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - viewModel.accounts.observe(viewLifecycleOwner) { updateBalances(it) } - viewModel.financing.observe(viewLifecycleOwner) { updatePendingFinances() } - viewModel.bmlLoanDetails.observe(viewLifecycleOwner) { updatePendingFinances() } + viewModel.accounts.observe(viewLifecycleOwner) { + updateBalances(it) + updateAttentionRow() + } + viewModel.financing.observe(viewLifecycleOwner) { + updatePendingFinances() + updateAttentionRow() + } + viewModel.bmlLoanDetails.observe(viewLifecycleOwner) { + updatePendingFinances() + updateAttentionRow() + } viewModel.bmlLimits.observe(viewLifecycleOwner) { updateForeignLimits(it) } viewModel.hideAmounts.observe(viewLifecycleOwner) { updateBalances(viewModel.accounts.value ?: emptyList()) updatePendingFinances() updateForeignLimits(viewModel.bmlLimits.value ?: emptyList()) + updateAttentionRow() } binding.swipeRefresh.setOnRefreshListener { @@ -76,6 +86,10 @@ class DashboardFragment : Fragment() { (activity as? HomeActivity)?.navigateTo(R.id.nav_finances) } + binding.cardOverdue.setOnClickListener { + (activity as? HomeActivity)?.navigateTo(R.id.nav_finances) + } + val cardAdapter = DashboardCardAdapter() binding.rvCards.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false) binding.rvCards.adapter = cardAdapter @@ -244,6 +258,56 @@ class DashboardFragment : Fragment() { } } + private fun updateAttentionRow() { + val hide = viewModel.hideAmounts.value ?: false + val accounts = viewModel.accounts.value ?: emptyList() + + // Blocked: sum across CASA-style accounts (exclude cards and loans) per currency. + val blockedByCurrency = accounts + .filter { it.profileType != "BML_CREDIT" && it.profileType != "BML_PREPAID" && it.profileType != "BML_DEBIT" && it.profileType != "BML_LOAN" } + .mapNotNull { acc -> + val v = acc.blockedAmount.replace(",", "").toDoubleOrNull() ?: 0.0 + if (v > 0.0) acc.currencyName.uppercase() to v else null + } + .groupBy({ it.first }, { it.second }) + .mapValues { (_, vs) -> vs.sum() } + + val blockedTotal = blockedByCurrency.values.sum() + if (blockedTotal > 0.0) { + // Primary line: prefer MVR if present, otherwise the first currency. + val primaryCcy = if ("MVR" in blockedByCurrency) "MVR" else blockedByCurrency.keys.first() + val primaryAmt = blockedByCurrency.getValue(primaryCcy) + binding.tvBlockedTotal.text = if (hide) "$primaryCcy ••••••" else "$primaryCcy %,.2f".format(primaryAmt) + + val secondary = blockedByCurrency.filterKeys { it != primaryCcy } + if (secondary.isNotEmpty()) { + binding.tvBlockedSecondary.text = secondary.entries.joinToString(" · ") { (ccy, amt) -> + if (hide) "$ccy ••••••" else "$ccy %,.2f".format(amt) + } + binding.tvBlockedSecondary.visibility = View.VISIBLE + } else { + binding.tvBlockedSecondary.visibility = View.GONE + } + binding.cardBlocked.visibility = View.VISIBLE + } else { + binding.cardBlocked.visibility = View.GONE + } + + // Overdue: MIB finance deals + BML loan details (assumed MVR — matches existing Pending Finances). + val mibOverdue = (viewModel.financing.value ?: emptyList()).sumOf { it.overdueAmount } + val bmlOverdue = (viewModel.bmlLoanDetails.value ?: emptyMap()).values.sumOf { it.overdueAmount } + val overdueTotal = mibOverdue + bmlOverdue + if (overdueTotal > 0.0) { + binding.tvOverdueTotal.text = if (hide) "MVR ••••••" else "MVR %,.2f".format(overdueTotal) + binding.cardOverdue.visibility = View.VISIBLE + } else { + binding.cardOverdue.visibility = View.GONE + } + + binding.rowAttention.visibility = + if (blockedTotal > 0.0 || overdueTotal > 0.0) View.VISIBLE else View.GONE + } + private fun updatePendingFinances() { val hide = viewModel.hideAmounts.value ?: false val mibTotal = (viewModel.financing.value ?: emptyList()).sumOf { it.outstandingAmount } diff --git a/app/src/main/java/sh/sar/basedbank/util/mibapi/MibAccountParser.kt b/app/src/main/java/sh/sar/basedbank/util/mibapi/MibAccountParser.kt index 717bcf5..d3d7a48 100644 --- a/app/src/main/java/sh/sar/basedbank/util/mibapi/MibAccountParser.kt +++ b/app/src/main/java/sh/sar/basedbank/util/mibapi/MibAccountParser.kt @@ -5,12 +5,16 @@ import sh.sar.basedbank.util.AccountListDisplay object MibAccountParser { - fun displayData(account: BankAccount) = AccountListDisplay( - name = account.accountBriefName, - number = account.accountNumber, - typeLabel = productLabel(account.accountTypeName), - balance = "${account.currencyName} ${account.availableBalance}" - ) + fun displayData(account: BankAccount): AccountListDisplay { + val blocked = account.blockedAmount.toDoubleOrNull() ?: 0.0 + return AccountListDisplay( + name = account.accountBriefName, + number = account.accountNumber, + typeLabel = productLabel(account.accountTypeName), + balance = "${account.currencyName} ${account.availableBalance}", + blockedBalance = if (blocked > 0.0) "${account.currencyName} ${account.blockedAmount}" else null + ) + } /** * Returns a display-ready product label for a MIB (Faisanet) account type name. diff --git a/app/src/main/java/sh/sar/basedbank/util/mibapi/MibHistoryParser.kt b/app/src/main/java/sh/sar/basedbank/util/mibapi/MibHistoryParser.kt index b7197c5..b1b60d0 100644 --- a/app/src/main/java/sh/sar/basedbank/util/mibapi/MibHistoryParser.kt +++ b/app/src/main/java/sh/sar/basedbank/util/mibapi/MibHistoryParser.kt @@ -5,13 +5,16 @@ import sh.sar.basedbank.util.AccountHistoryDisplay object MibHistoryParser { - fun displayData(account: BankAccount) = AccountHistoryDisplay( - name = account.accountBriefName, - number = account.accountNumber, - bankPill = null, // MIB has no bank pill - typeLabel = MibAccountParser.productLabel(account.accountTypeName), - availableBalance = "${account.currencyName} ${account.availableBalance}", - workingBalance = "${account.currencyName} ${account.currentBalance}", - blockedBalance = null - ) + fun displayData(account: BankAccount): AccountHistoryDisplay { + val blocked = account.blockedAmount.toDoubleOrNull() ?: 0.0 + return AccountHistoryDisplay( + name = account.accountBriefName, + number = account.accountNumber, + bankPill = null, // MIB has no bank pill + typeLabel = MibAccountParser.productLabel(account.accountTypeName), + availableBalance = "${account.currencyName} ${account.availableBalance}", + workingBalance = "${account.currencyName} ${account.currentBalance}", + blockedBalance = if (blocked > 0.0) "${account.currencyName} ${account.blockedAmount}" else null + ) + } } diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml index d25b79b..f825edf 100644 --- a/app/src/main/res/layout/fragment_dashboard.xml +++ b/app/src/main/res/layout/fragment_dashboard.xml @@ -176,6 +176,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cards Available Balance %1$s blocked + Blocked Funds + Overdue Financing Quick Transfer