fix some currency detection issues with MIB to MIB, and also Option to automatically add contact during BML to MIB USD transfer #35 (its not fully auto)
Auto Tag on Version Change / check-version (push) Failing after 13m51s
Auto Tag on Version Change / check-version (push) Failing after 13m51s
This commit is contained in:
@@ -71,7 +71,8 @@ class BmlValidateClient {
|
||||
originalInput = account,
|
||||
name = root.optString("name"),
|
||||
alias = null,
|
||||
currency = "MVR",
|
||||
// BML's MIB verify endpoint doesn't return the MIB account's currency.
|
||||
currency = "",
|
||||
agnt = root.optString("agnt").takeIf { it.isNotBlank() }
|
||||
)
|
||||
} catch (_: Exception) { null }
|
||||
|
||||
@@ -42,7 +42,8 @@ data class MibTransferResult(
|
||||
data class MibIpsAccountInfo(
|
||||
val accountName: String,
|
||||
val accountNumber: String,
|
||||
val bankId: String
|
||||
val bankId: String,
|
||||
val currency: String = "" // "MVR", "USD", or "" if unknown
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -130,7 +130,10 @@ class MibTransferClient {
|
||||
MibIpsAccountInfo(
|
||||
accountName = json.optString("accountName").trim(),
|
||||
accountNumber = accountNumber,
|
||||
bankId = json.optString("bankBic")
|
||||
bankId = json.optString("bankBic"),
|
||||
// MIB IPS only returns success for MVR cross-bank accounts;
|
||||
// USD cross-bank accounts fail this lookup entirely.
|
||||
currency = "MVR"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -156,10 +159,18 @@ class MibTransferClient {
|
||||
// accountName may be at root or inside a "data" object
|
||||
val name = json.optString("accountName").takeIf { it.isNotBlank() }
|
||||
?: json.optJSONObject("data")?.optString("accountName") ?: ""
|
||||
val currencyCode = json.optString("currencyCode").takeIf { it.isNotBlank() }
|
||||
?: json.optJSONObject("data")?.optString("currencyCode") ?: ""
|
||||
val currency = when (currencyCode) {
|
||||
"840" -> "USD"
|
||||
"462" -> "MVR"
|
||||
else -> ""
|
||||
}
|
||||
MibIpsAccountInfo(
|
||||
accountName = name.trim(),
|
||||
accountNumber = accountNumber,
|
||||
bankId = "MADVMVMV" // MIB
|
||||
bankId = "MADVMVMV", // MIB
|
||||
currency = currency
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,52 @@ class AddContactSheetFragment : BottomSheetDialogFragment() {
|
||||
categories = cats.filter { it.id != "BML" }
|
||||
if (selectedDest?.isBml == false) setupCategoryDropdown()
|
||||
}
|
||||
|
||||
applyPrefillArgs()
|
||||
}
|
||||
|
||||
private fun applyPrefillArgs() {
|
||||
val args = arguments ?: return
|
||||
val bmlProfileId = args.getString(ARG_BML_PROFILE_ID)
|
||||
val accountNumber = args.getString(ARG_ACCOUNT_NUMBER)
|
||||
val recipientName = args.getString(ARG_RECIPIENT_NAME)
|
||||
val currency = args.getString(ARG_CURRENCY)
|
||||
|
||||
if (bmlProfileId != null) {
|
||||
val match = destinations.firstOrNull { it.isBml && it.bmlLoginId == bmlProfileId }
|
||||
if (match != null) {
|
||||
selectedDest = match
|
||||
binding.actvDestination.setText(match.label, false)
|
||||
updateMibOnlyVisibility()
|
||||
}
|
||||
}
|
||||
|
||||
if (accountNumber != null) {
|
||||
binding.etAccount.setText(accountNumber)
|
||||
}
|
||||
|
||||
// Skip lookup only when we have a MIB-verified name+currency from the caller.
|
||||
if (selectedDest != null && accountNumber != null &&
|
||||
!recipientName.isNullOrBlank() && !currency.isNullOrBlank()
|
||||
) {
|
||||
val bankBic = when {
|
||||
accountNumber.matches(Regex("^9\\d{16}$")) -> "MADVMVMV"
|
||||
accountNumber.matches(Regex("^7\\d{12}$")) -> "MALBMVMV"
|
||||
else -> ""
|
||||
}
|
||||
val trnType = if (accountNumber.matches(Regex("^9\\d{16}$"))) "DOT" else "IAT"
|
||||
val validation = BmlAccountValidation(
|
||||
trnType = trnType,
|
||||
validationType = "prefilled",
|
||||
account = accountNumber,
|
||||
originalInput = accountNumber,
|
||||
name = recipientName,
|
||||
alias = null,
|
||||
currency = currency,
|
||||
agnt = bankBic.takeIf { it.isNotBlank() }
|
||||
)
|
||||
showLookupResult(validation, accountNumber)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildDestinations(): List<DestinationOption> {
|
||||
@@ -517,5 +563,24 @@ class AddContactSheetFragment : BottomSheetDialogFragment() {
|
||||
companion object {
|
||||
// BML's internal UUID for MIB bank — used as the "swift" field when saving DOT contacts
|
||||
private const val MIB_SWIFT_ON_BML = "F4E79935-3E73-E611-80DD-00155D020F0A"
|
||||
|
||||
private const val ARG_BML_PROFILE_ID = "bml_profile_id"
|
||||
private const val ARG_ACCOUNT_NUMBER = "account_number"
|
||||
private const val ARG_RECIPIENT_NAME = "recipient_name"
|
||||
private const val ARG_CURRENCY = "currency"
|
||||
|
||||
fun newInstance(
|
||||
bmlProfileId: String? = null,
|
||||
accountNumber: String? = null,
|
||||
recipientName: String? = null,
|
||||
currency: String? = null
|
||||
) = AddContactSheetFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
if (bmlProfileId != null) putString(ARG_BML_PROFILE_ID, bmlProfileId)
|
||||
if (accountNumber != null) putString(ARG_ACCOUNT_NUMBER, accountNumber)
|
||||
if (recipientName != null) putString(ARG_RECIPIENT_NAME, recipientName)
|
||||
if (currency != null) putString(ARG_CURRENCY, currency)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ class TransferFragment : Fragment() {
|
||||
private var resolvedAccountNumber = ""
|
||||
private var resolvedRecipientName = ""
|
||||
private var resolvedBankName = ""
|
||||
private var resolvedDestCurrency = "" // "MVR" / "USD" / "" if unknown
|
||||
private var resolvedToOwnAccount: BankAccount? = null
|
||||
|
||||
// Selected Fahipay service when source is Fahipay and destination is a phone number
|
||||
@@ -661,6 +662,7 @@ class TransferFragment : Fragment() {
|
||||
}
|
||||
resolvedAccountNumber = ""
|
||||
resolvedRecipientName = ""
|
||||
resolvedDestCurrency = ""
|
||||
resolvedToOwnAccount = null
|
||||
selectedFahipayService = null
|
||||
binding.cardToInfo.visibility = View.GONE
|
||||
@@ -677,6 +679,7 @@ class TransferFragment : Fragment() {
|
||||
if (binding.cardToInfo.visibility == View.VISIBLE) {
|
||||
resolvedAccountNumber = ""
|
||||
resolvedRecipientName = ""
|
||||
resolvedDestCurrency = ""
|
||||
resolvedToOwnAccount = null
|
||||
binding.cardToInfo.visibility = View.GONE
|
||||
binding.tilTo.visibility = View.VISIBLE
|
||||
@@ -760,7 +763,16 @@ class TransferFragment : Fragment() {
|
||||
"IAT" -> "MALBMVMV"
|
||||
else -> bmlResult.agnt ?: bmlResult.account
|
||||
}
|
||||
MibIpsAccountInfo(accountName = bmlResult.name, accountNumber = bmlResult.account, bankId = bankId)
|
||||
// BML's MIB verify endpoint doesn't return the account's currency.
|
||||
// Enrich via MIB lookup when a MIB session is available.
|
||||
val currency = if (
|
||||
inputType == AccountInputParser.InputType.MIB_ACCOUNT &&
|
||||
bmlResult.currency.isBlank() && mibSess != null
|
||||
) {
|
||||
try { MibTransferClient().lookup(mibSess, bmlResult.account).currency }
|
||||
catch (_: Exception) { "" }
|
||||
} else bmlResult.currency
|
||||
MibIpsAccountInfo(accountName = bmlResult.name, accountNumber = bmlResult.account, bankId = bankId, currency = currency)
|
||||
} else if (mibSess != null) {
|
||||
try { MibTransferClient().lookup(mibSess, accountNumber) }
|
||||
catch (e: MibLookupException) { errorMsg = e.message; null }
|
||||
@@ -769,21 +781,29 @@ class TransferFragment : Fragment() {
|
||||
errorMsg = getString(R.string.transfer_account_not_found); null
|
||||
}
|
||||
} else {
|
||||
if (mibSess != null) {
|
||||
val mibInfo = if (mibSess != null) {
|
||||
try { MibTransferClient().lookup(mibSess, accountNumber) }
|
||||
catch (e: MibLookupException) { errorMsg = e.message; null }
|
||||
catch (_: Exception) { errorMsg = getString(R.string.transfer_account_not_found); null }
|
||||
} else {
|
||||
val bmlResult = try { BmlValidateClient().validateAccount(bmlSess!!, accountNumber) } catch (_: Exception) { null }
|
||||
} else null
|
||||
if (mibInfo != null) {
|
||||
mibInfo
|
||||
} else if (bmlSess != null) {
|
||||
val bmlResult = try { BmlValidateClient().validateAccount(bmlSess, accountNumber) } catch (_: Exception) { null }
|
||||
if (bmlResult != null) {
|
||||
errorMsg = null
|
||||
val bankId = when (bmlResult.trnType) {
|
||||
"IAT" -> "MALBMVMV"
|
||||
else -> bmlResult.agnt ?: bmlResult.account
|
||||
}
|
||||
MibIpsAccountInfo(accountName = bmlResult.name, accountNumber = bmlResult.account, bankId = bankId)
|
||||
MibIpsAccountInfo(accountName = bmlResult.name, accountNumber = bmlResult.account, bankId = bankId, currency = bmlResult.currency)
|
||||
} else {
|
||||
errorMsg = getString(R.string.transfer_account_not_found); null
|
||||
if (errorMsg == null) errorMsg = getString(R.string.transfer_account_not_found)
|
||||
null
|
||||
}
|
||||
} else {
|
||||
if (errorMsg == null) errorMsg = getString(R.string.transfer_account_not_found)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -799,6 +819,7 @@ class TransferFragment : Fragment() {
|
||||
resolvedAccountNumber = info.accountNumber
|
||||
resolvedRecipientName = info.accountName
|
||||
resolvedBankName = info.bankId
|
||||
resolvedDestCurrency = info.currency
|
||||
savedToSubtitle = "${info.accountNumber} · ${info.bankId}"
|
||||
savedToColorHex = colorHex
|
||||
savedToImageHash = when {
|
||||
@@ -979,6 +1000,7 @@ class TransferFragment : Fragment() {
|
||||
private fun prefillToFromContact(accountNumber: String, label: String) {
|
||||
resolvedAccountNumber = ""
|
||||
resolvedRecipientName = ""
|
||||
resolvedDestCurrency = ""
|
||||
binding.cardToInfo.visibility = View.GONE
|
||||
binding.tilTo.visibility = View.VISIBLE
|
||||
binding.btnPickContact.visibility = View.VISIBLE
|
||||
@@ -1115,10 +1137,27 @@ class TransferFragment : Fragment() {
|
||||
if (isSrcBml && isDestMib && currency == "USD") {
|
||||
val hasBmlContact = allContacts.any { it.benefCategoryId == "BML" && it.benefAccount == resolvedAccountNumber }
|
||||
if (!hasBmlContact) {
|
||||
// If we verified the dest currency via MIB fallback, the block is purely a BML API limitation.
|
||||
// Otherwise (no MIB session, currency truly unknown) the generic message applies.
|
||||
val msgRes = if (resolvedDestCurrency.isNotBlank())
|
||||
R.string.transfer_bml_contact_required_msg_bml_limit
|
||||
else
|
||||
R.string.transfer_bml_contact_required_msg
|
||||
val bmlProfileId = src.profileId.takeIf { it.isNotBlank() }
|
||||
?: src.loginTag.removePrefix("bml_").takeIf { it.isNotBlank() }
|
||||
val verified = resolvedDestCurrency.isNotBlank()
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.transfer_bml_contact_required_title)
|
||||
.setMessage(R.string.transfer_bml_contact_required_msg)
|
||||
.setPositiveButton(R.string.close, null)
|
||||
.setMessage(msgRes)
|
||||
.setPositiveButton(R.string.contact_save) { _, _ ->
|
||||
AddContactSheetFragment.newInstance(
|
||||
bmlProfileId = bmlProfileId,
|
||||
accountNumber = resolvedAccountNumber,
|
||||
recipientName = if (verified) resolvedRecipientName else null,
|
||||
currency = if (verified) resolvedDestCurrency else null
|
||||
).show(parentFragmentManager, "add_contact")
|
||||
}
|
||||
.setNegativeButton(R.string.close, null)
|
||||
.show()
|
||||
return
|
||||
}
|
||||
@@ -1128,11 +1167,13 @@ class TransferFragment : Fragment() {
|
||||
val bankNameCapture = resolvedBankName
|
||||
val capturedToAvatar = (binding.ivToPhoto.drawable as? android.graphics.drawable.BitmapDrawable)?.bitmap
|
||||
|
||||
val destCurrency = allAccounts.firstOrNull { it.accountNumber == resolvedAccountNumber }
|
||||
?.currencyName?.ifBlank { "MVR" }
|
||||
?: allContacts.firstOrNull { it.benefAccount == resolvedAccountNumber }
|
||||
?.transferCyDesc?.ifBlank { "MVR" }
|
||||
?: if (isDestMib) "MVR" else "MVR"
|
||||
val destCurrency = resolvedDestCurrency.ifBlank {
|
||||
allAccounts.firstOrNull { it.accountNumber == resolvedAccountNumber }
|
||||
?.currencyName?.ifBlank { "MVR" }
|
||||
?: allContacts.firstOrNull { it.benefAccount == resolvedAccountNumber }
|
||||
?.transferCyDesc?.ifBlank { "MVR" }
|
||||
?: "MVR"
|
||||
}
|
||||
val isUsdToMvr = currency.equals("USD", ignoreCase = true) && destCurrency.equals("MVR", ignoreCase = true)
|
||||
val isSrcCredit = src.profileType == "BML_CREDIT"
|
||||
|
||||
@@ -2030,6 +2071,7 @@ class TransferFragment : Fragment() {
|
||||
resolvedAccountNumber = ""
|
||||
resolvedRecipientName = ""
|
||||
resolvedBankName = ""
|
||||
resolvedDestCurrency = ""
|
||||
resolvedToOwnAccount = null
|
||||
selectedFahipayService = null
|
||||
binding.cardToInfo.visibility = View.GONE
|
||||
|
||||
@@ -280,7 +280,8 @@
|
||||
<string name="transfer_confirm">Confirm</string>
|
||||
<string name="transfer_success">Transfer Successful</string>
|
||||
<string name="transfer_bml_contact_required_title">Contact Required</string>
|
||||
<string name="transfer_bml_contact_required_msg">To send USD to a MIB account from BML, the recipient must be saved as a BML contact first. This is required by BML\'s API.\n\nPlease add this account as a BML contact, then try again.</string>
|
||||
<string name="transfer_bml_contact_required_msg">We couldn\'t verify the recipient\'s currency for this transfer.\n\nPlease save them as a contact, manually select the correct currency, then try again.</string>
|
||||
<string name="transfer_bml_contact_required_msg_bml_limit">BML\'s API requires the recipient to be saved as a contact when sending USD to a non-BML account, even though we verified the account.\n\nPlease save them as a contact, then try again.</string>
|
||||
<string name="transfer_missing_internal_id">Account data is incomplete — please re-login to refresh.</string>
|
||||
<string name="transfer_verify_payment">Verify Payment</string>
|
||||
<string name="transfer_send_otp_via">Send verification code via</string>
|
||||
|
||||
Reference in New Issue
Block a user