diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt index 130e8f3..d36d029 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt @@ -52,6 +52,7 @@ import sh.sar.basedbank.databinding.ItemPickerSectionHeaderBinding import sh.sar.basedbank.databinding.ItemTransferTypeBinding import sh.sar.basedbank.ui.home.transfer.BmlTransferHandler import sh.sar.basedbank.ui.home.transfer.BmlVerifiedCards +import sh.sar.basedbank.ui.home.transfer.CardPayoutService import sh.sar.basedbank.ui.home.transfer.CardPayoutTransferHandler import sh.sar.basedbank.ui.home.transfer.CarrierLookup import sh.sar.basedbank.ui.home.transfer.FahipayService @@ -407,7 +408,7 @@ class TransferFragment : Fragment() { return@setFragmentResultListener } val label = bundle.getString(ContactPickerSheetFragment.KEY_LABEL) ?: "" - if (applyFahipayContact(accountNumber, bundle.getString(ContactPickerSheetFragment.KEY_CATEGORY), label)) { + if (applyServiceContact(accountNumber, bundle.getString(ContactPickerSheetFragment.KEY_CATEGORY), label)) { return@setFragmentResultListener } val subtitle = bundle.getString(ContactPickerSheetFragment.KEY_SUBTITLE) ?: accountNumber @@ -453,7 +454,7 @@ class TransferFragment : Fragment() { // Pre-select contact if navigated from contacts page or QR scan arguments?.getString(ARG_ACCOUNT)?.let { account -> val name = arguments?.getString(ARG_NAME) ?: account - if (applyFahipayContact(account, arguments?.getString(ARG_CONTACT_CATEGORY), name)) return@let + if (applyServiceContact(account, arguments?.getString(ARG_CONTACT_CATEGORY), name)) return@let prefillToDirectly( accountNumber = account, displayName = name, @@ -1141,15 +1142,27 @@ class TransferFragment : Fragment() { } /** - * A saved Fahipay favourite: its list ([categoryId]) already says which service pays it, so - * that service is applied as the only transfer type, with no carrier lookup. That switches - * the source to the Fahipay wallet and brings in the service's amount rules, the same as a - * searched number. Returns false, doing nothing, when [categoryId] isn't a Fahipay list. + * A saved Fahipay favourite, or a recent paid with a Fahipay or card service: its category + * ([categoryId]) already says which service pays it, so that service is applied as the only + * transfer type, with no carrier lookup. That switches the source to the Fahipay wallet or + * the default card and brings in the service's amount rules, the same as a searched number. + * Returns false, doing nothing, when [categoryId] isn't one of those. */ - private fun applyFahipayContact(number: String, categoryId: String?, name: String): Boolean { - val service = FahipayService.fromContactCategory(categoryId) ?: return false + private fun applyServiceContact(number: String, categoryId: String?, name: String): Boolean { + val ownerName = name.takeIf { it.isNotBlank() && it != number } + val type = FahipayService.fromContactCategory(categoryId)?.let { TransferType.Fahipay(it, ownerName) } + ?: CardPayoutService.fromContactCategory(categoryId)?.let { service -> + // The cards that could pay it then may not be payable now + val cards = cardPayoutHandler().payableCards() + if (cards.isEmpty()) { + Toast.makeText(requireContext(), R.string.bml_card_pay_no_verified, Toast.LENGTH_LONG).show() + return true + } + TransferType.Card(service, ownerName, cards) + } + ?: return false clearRecipient() - offerTransferTypes(number, listOf(TransferType.Fahipay(service, name.takeIf { it.isNotBlank() && it != number }))) + offerTransferTypes(number, listOf(type)) return true } diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/transfer/CardPayoutTransferHandler.kt b/app/src/main/java/sh/sar/basedbank/ui/home/transfer/CardPayoutTransferHandler.kt index 99ced62..15da13f 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/transfer/CardPayoutTransferHandler.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/transfer/CardPayoutTransferHandler.kt @@ -33,22 +33,37 @@ enum class CardPayoutService( override val maxAmount: Int?, override val decimalsAllowed: Boolean, override val gstPercent: Int?, + /** + * Tags the recents paid with this service (`RecentPick.contactCategory`), so picking one + * again pays the same way. Not a real contact list: card payouts have no favourites. + */ + val contactCategory: String, override val gstAdded: Boolean = false, ) : PayoutService { DHIRAAGU_RELOAD("Dhiraagu Reload", "Dhiraagu · Reload", R.drawable.dhiraagu_logo, - minAmount = 20, maxAmount = 1000, decimalsAllowed = false, gstPercent = 8) { + minAmount = 20, maxAmount = 1000, decimalsAllowed = false, gstPercent = 8, + contactCategory = "CARD_DHIRAAGU_RELOAD") { // Dhiraagu rounds the GST to 2 places and credits the rest override fun creditedAfterGst(amount: BigDecimal): BigDecimal = amount - DhiraaguPaymentClient.gstOf(amount.setScale(0, RoundingMode.DOWN).toInt()) }, // Easy Pay sets no limits of its own: any amount with up to 2 decimals, no GST DHIRAAGU_BILL("Dhiraagu Bill Pay", "Dhiraagu · Bill Pay", R.drawable.dhiraagu_logo, - minAmount = 1, maxAmount = null, decimalsAllowed = true, gstPercent = null), + minAmount = 1, maxAmount = null, decimalsAllowed = true, gstPercent = null, + contactCategory = "CARD_DHIRAAGU_BILL"), // Ooredoo credits the whole amount and charges the card 8% GST on top OOREDOO_RAASTAS("Raastas", "Ooredoo · Raastas", R.drawable.ooredoo_logo, - minAmount = 20, maxAmount = null, decimalsAllowed = false, gstPercent = 8, gstAdded = true), + minAmount = 20, maxAmount = null, decimalsAllowed = false, gstPercent = 8, + contactCategory = "CARD_RAASTAS", gstAdded = true), OOREDOO_BILL("Ooredoo Bill Pay", "Ooredoo · Bill Pay", R.drawable.ooredoo_logo, - minAmount = 10, maxAmount = null, decimalsAllowed = true, gstPercent = null); + minAmount = 10, maxAmount = null, decimalsAllowed = true, gstPercent = null, + contactCategory = "CARD_OOREDOO_BILL"); + + companion object { + /** The service a recent was paid with, from its [contactCategory], or null when it isn't one. */ + fun fromContactCategory(categoryId: String?): CardPayoutService? = + entries.firstOrNull { it.contactCategory == categoryId } + } } /** @@ -136,7 +151,8 @@ class CardPayoutTransferHandler( displayName = displayName, subtitle = "${type.label} · $number", colorHex = "#E4002B", - imageHash = null + imageHash = null, + contactCategory = type.service.contactCategory ) fragment.focusAmount() } diff --git a/docs/thijooree/20-transfer-flows.md b/docs/thijooree/20-transfer-flows.md index 02184c9..03d19e8 100644 --- a/docs/thijooree/20-transfer-flows.md +++ b/docs/thijooree/20-transfer-flows.md @@ -189,6 +189,13 @@ paid ("You pay MVR 21.60 with 8% GST"). The order check in step 2 below compares **Reference.** None. The field is cleared and disabled, as for the Fahipay services. +**Recents.** As with Fahipay services, the recent is saved with the service's category +(`CardPayoutService.contactCategory`: `CARD_DHIRAAGU_RELOAD`, `CARD_DHIRAAGU_BILL`, +`CARD_RAASTAS`, `CARD_OOREDOO_BILL`). Picking it again applies that service with no lookup, so +the default card (or another payable card) is selected rather than the default account +(`TransferFragment.applyServiceContact`). With no payable card left, it toasts and stops. A number +keeps one recent, so paying it another way replaces the category. + **Sending.** The only part that differs from paying a card-only BML merchant link is where the BML transaction comes from: