TP-66207 | Added fix for mandate screen for create and approve mandate (#10773)
This commit is contained in:
@@ -154,8 +154,8 @@ fun BankAccountItem(
|
||||
bankAccountEligibilityState: EligibilityState,
|
||||
onSelected: (LinkedAccountEntity) -> Unit,
|
||||
isSelected: Boolean = false,
|
||||
isExpandableItem: Boolean = false, // TODO This to be removed
|
||||
isSeeMoreEnabled: Boolean = true, // TODO This to be removed
|
||||
isExpandableItem: Boolean = false, // TODO to be removed after mandate UI enhancement
|
||||
isSeeMoreEnabled: Boolean = true // TODO to be removed after mandate UI enhancement
|
||||
) {
|
||||
val isItemClickable =
|
||||
if (isExpandableItem) {
|
||||
|
||||
@@ -1360,11 +1360,6 @@ constructor(
|
||||
|
||||
initPaymentAmount()
|
||||
|
||||
// _bankSelectionEnabled.update { // TODO: check this
|
||||
// source !is SendMoneyScreenSource.CollectRequest && linkedAccounts.size
|
||||
// > 1
|
||||
// }
|
||||
|
||||
checkIfPSPIsDown()
|
||||
|
||||
// TODO: Move activity intent data to VM & then this entire function to VM init
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
package com.navi.pay.management.mandate.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -17,11 +16,14 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Checkbox
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ModalBottomSheetState
|
||||
import androidx.compose.material.ModalBottomSheetValue
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -66,6 +68,7 @@ import com.navi.pay.destinations.MandateDetailScreenOfPendingCategoryDestination
|
||||
import com.navi.pay.destinations.MandateSummaryScreenDestination
|
||||
import com.navi.pay.entry.NaviPayActivity
|
||||
import com.navi.pay.management.common.sendmoney.model.view.PayeeEntity
|
||||
import com.navi.pay.management.common.sendmoney.ui.BankAccountItem
|
||||
import com.navi.pay.management.common.sendmoney.ui.BankAccountsListSection
|
||||
import com.navi.pay.management.mandate.model.view.MandateAmountRule
|
||||
import com.navi.pay.management.mandate.model.view.MandateCategory
|
||||
@@ -258,79 +261,89 @@ fun RenderMandateDetailScreenOfPendingCategory(
|
||||
)
|
||||
}
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
NaviPayHeader(
|
||||
title = stringResource(id = R.string.autopay_details),
|
||||
onNavigationIconClick = {
|
||||
resultNavigator.navigateBack(result = MandateScreenBackNavigation.NotRefresh)
|
||||
},
|
||||
onActionClick = {
|
||||
coroutineScope.launch {
|
||||
mandateDetailOfPendingCategoryViewModel.updateBottomSheetUIState(
|
||||
bottomSheetUIState =
|
||||
MandateDetailOfPendingCategoryBottomSheetState.OptionsMenu,
|
||||
showBottomSheet = true,
|
||||
allowStateChange = true
|
||||
Scaffold(
|
||||
topBar = {
|
||||
NaviPayHeader(
|
||||
title = stringResource(id = R.string.autopay_details),
|
||||
onNavigationIconClick = {
|
||||
resultNavigator.navigateBack(
|
||||
result = MandateScreenBackNavigation.NotRefresh
|
||||
)
|
||||
}
|
||||
},
|
||||
actionIconId =
|
||||
if (mandateDetailOfPendingCategoryViewModel.shouldMenuOptionIconBeVisible)
|
||||
R.drawable.ic_three_dots_option_icon
|
||||
else -1,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
},
|
||||
onActionClick = {
|
||||
coroutineScope.launch {
|
||||
mandateDetailOfPendingCategoryViewModel.updateBottomSheetUIState(
|
||||
bottomSheetUIState =
|
||||
MandateDetailOfPendingCategoryBottomSheetState.OptionsMenu,
|
||||
showBottomSheet = true,
|
||||
allowStateChange = true
|
||||
)
|
||||
}
|
||||
},
|
||||
actionIconId =
|
||||
if (mandateDetailOfPendingCategoryViewModel.shouldMenuOptionIconBeVisible)
|
||||
R.drawable.ic_three_dots_option_icon
|
||||
else -1,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.padding(it).fillMaxWidth().verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
MandateDetailBasicInfoCard(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
isMandateOfPendingType = true,
|
||||
mandateEntity = mandateEntity
|
||||
)
|
||||
|
||||
MandateDetailBasicInfoCard(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
isMandateOfPendingType = true,
|
||||
mandateEntity = mandateEntity
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
MandateDetailPayDateInfoCard(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
mandateEntity = mandateEntity
|
||||
)
|
||||
|
||||
MandateDetailPayDateInfoCard(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
mandateEntity = mandateEntity
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
MandateDetailsStartEndDateInfoCard(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
showStatus = false,
|
||||
mandateEntity = mandateEntity
|
||||
)
|
||||
|
||||
MandateDetailsStartEndDateInfoCard(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
showStatus = false,
|
||||
mandateEntity = mandateEntity
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
Box {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
}
|
||||
},
|
||||
bottomBar = {
|
||||
AccountSelectionFooter(
|
||||
mandateDetailOfPendingCategoryViewModel =
|
||||
mandateDetailOfPendingCategoryViewModel,
|
||||
onDeclineButtonClicked = onDeclineButtonClicked,
|
||||
onApproveButtonClicked = onApproveButtonClicked,
|
||||
)
|
||||
|
||||
SuccessSnackBar(
|
||||
modifier =
|
||||
Modifier.align(Alignment.BottomCenter)
|
||||
.padding(horizontal = 16.dp, vertical = 32.dp),
|
||||
show = snackBarState,
|
||||
snackBarConfig =
|
||||
SnackBarPredefinedConfig.successConfig(
|
||||
title = stringResource(id = R.string.user_blocked_successfully),
|
||||
),
|
||||
onDismissed = {
|
||||
mandateDetailOfPendingCategoryViewModel.updateShowSnackBarState(
|
||||
showSnackBar = false
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
snackbarHost = {
|
||||
if (snackBarState) {
|
||||
SuccessSnackBar(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 32.dp),
|
||||
show = snackBarState,
|
||||
snackBarConfig =
|
||||
SnackBarPredefinedConfig.successConfig(
|
||||
title = stringResource(id = R.string.user_blocked_successfully),
|
||||
),
|
||||
onDismissed = {
|
||||
mandateDetailOfPendingCategoryViewModel.updateShowSnackBarState(
|
||||
showSnackBar = false
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,7 +359,7 @@ fun AccountSelectionFooter(
|
||||
val selectedBankAccount by
|
||||
mandateDetailOfPendingCategoryViewModel.selectedBankAccount.collectAsStateWithLifecycle()
|
||||
val showAllBankListView by
|
||||
mandateDetailOfPendingCategoryViewModel.showAllBankList.collectAsStateWithLifecycle()
|
||||
mandateDetailOfPendingCategoryViewModel.showAllBankListView.collectAsStateWithLifecycle()
|
||||
val isWarningConsentChecked by
|
||||
mandateDetailOfPendingCategoryViewModel.isWarningConsentChecked
|
||||
.collectAsStateWithLifecycle()
|
||||
@@ -401,16 +414,35 @@ fun AccountSelectionFooter(
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
BankAccountsListSection(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
bankAccountState = bankAccountState,
|
||||
selectedBankAccount = selectedBankAccount,
|
||||
onBankAccountSelected = {
|
||||
mandateDetailOfPendingCategoryViewModel.updateSelectedBankAccount(
|
||||
selectedBankAccount = it
|
||||
if (showAllBankListView) {
|
||||
BankAccountsListSection(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
bankAccountState = bankAccountState,
|
||||
selectedBankAccount = selectedBankAccount,
|
||||
onBankAccountSelected = {
|
||||
mandateDetailOfPendingCategoryViewModel.updateSelectedBankAccount(
|
||||
selectedBankAccount = it
|
||||
)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
selectedBankAccount?.let {
|
||||
BankAccountItem(
|
||||
bankAccount = it,
|
||||
bankAccountEligibilityState = it.eligibilityState,
|
||||
onSelected = {
|
||||
if (mandateDetailOfPendingCategoryViewModel.isPendingMandateOfTypeCreate) {
|
||||
mandateDetailOfPendingCategoryViewModel.updateAllBankListView(
|
||||
showAllBankList = true
|
||||
)
|
||||
}
|
||||
},
|
||||
isExpandableItem = true,
|
||||
isSeeMoreEnabled =
|
||||
mandateDetailOfPendingCategoryViewModel.isPendingMandateOfTypeCreate
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
|
||||
@@ -47,7 +47,6 @@ import com.navi.pay.onboarding.account.detail.model.view.LinkedAccountEntity
|
||||
import com.navi.pay.utils.DATE_TIME_FORMAT_DATE_MONTH_YEAR_WITHOUT_SEPARATOR
|
||||
import com.navi.pay.utils.DATE_TIME_FORMAT_YEAR_MONTH_DATE_WITH_SLASH_SEPARATOR
|
||||
import com.navi.pay.utils.REQUEST_TYPE_MANDATE
|
||||
import com.navi.pay.utils.RESOURCE_DEFAULT_ID
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Deferred
|
||||
@@ -103,8 +102,8 @@ constructor(
|
||||
private val _selectedBankAccount = MutableStateFlow<LinkedAccountEntity?>(null)
|
||||
val selectedBankAccount = _selectedBankAccount.asStateFlow()
|
||||
|
||||
private val _showAllBankList = MutableStateFlow(false)
|
||||
val showAllBankList = _showAllBankList.asStateFlow()
|
||||
private val _showAllABankList = MutableStateFlow(false)
|
||||
val showAllBankListView = _showAllABankList.asStateFlow()
|
||||
|
||||
private val _isWarningConsentChecked = MutableStateFlow(true)
|
||||
val isWarningConsentChecked = _isWarningConsentChecked.asStateFlow()
|
||||
@@ -159,7 +158,7 @@ constructor(
|
||||
initMandateEntity()
|
||||
|
||||
val asyncJobList = mutableListOf<Deferred<Unit>>()
|
||||
asyncJobList.add(async { fetchLinkedAccountBankList() })
|
||||
asyncJobList.add(async { executeLinkedAccountsFetchAndUpdateBankAccountState() })
|
||||
asyncJobList.add(async { validateVpaAndUpdateIsMerchantStateValues() })
|
||||
asyncJobList.awaitAll()
|
||||
|
||||
@@ -171,55 +170,91 @@ constructor(
|
||||
payeeEntity?.let { mandateEntity = it.toMandateEntity() }
|
||||
}
|
||||
|
||||
private suspend fun fetchLinkedAccountBankList() {
|
||||
val linkedAccounts = linkedAccountsUseCase.execute()
|
||||
private suspend fun executeLinkedAccountsFetchAndUpdateBankAccountState() {
|
||||
updateBankAccountsState(state = BankAccountsState.Loading)
|
||||
|
||||
if (linkedAccounts.isNotEmpty()) {
|
||||
val linkedAccountsInDB = linkedAccountsUseCase.execute()
|
||||
|
||||
val linkedAccountsWithEligibilityStateUpdated =
|
||||
linkedAccounts.map { linkedAccountEntity ->
|
||||
val eligibilityState =
|
||||
if (!linkedAccountEntity.isMPinSet) {
|
||||
EligibilityState(
|
||||
isAccountEligible = false,
|
||||
inEligibilityReasonResId = R.string.mpin_not_set
|
||||
)
|
||||
} else {
|
||||
val isCreditAccount =
|
||||
linkedAccountEntity.accountType == AccountType.CREDIT.name
|
||||
EligibilityState(
|
||||
isAccountEligible = !isCreditAccount, // CC not allowed for Mandate
|
||||
inEligibilityReasonResId =
|
||||
if (isCreditAccount)
|
||||
R.string.credit_cards_can_only_be_used_for_merchant_payments
|
||||
else RESOURCE_DEFAULT_ID
|
||||
)
|
||||
}
|
||||
val postProcessedLinkedAccounts =
|
||||
postProcessLinkedAccounts(linkedAccounts = linkedAccountsInDB)
|
||||
|
||||
linkedAccountEntity.eligibilityState = eligibilityState
|
||||
linkedAccountEntity
|
||||
}
|
||||
|
||||
updateBankAccountsState(
|
||||
state =
|
||||
BankAccountsState.AccountList(
|
||||
accounts = linkedAccountsWithEligibilityStateUpdated
|
||||
)
|
||||
)
|
||||
|
||||
updateSelectedBankAccount(
|
||||
selectedBankAccount =
|
||||
linkedAccountsWithEligibilityStateUpdated.singleOrNull { linkedAccountEntity ->
|
||||
linkedAccountEntity.vpaEntityList.any { vpaInfo ->
|
||||
vpaInfo.vpa == mandateEntity?.payerVpa
|
||||
}
|
||||
} ?: linkedAccountsWithEligibilityStateUpdated[0]
|
||||
)
|
||||
} else {
|
||||
if (postProcessedLinkedAccounts.isEmpty()) {
|
||||
updateBankAccountsState(state = BankAccountsState.NoAccountLinked)
|
||||
} else {
|
||||
updateBankAccountsState(
|
||||
state = BankAccountsState.AccountList(accounts = postProcessedLinkedAccounts)
|
||||
)
|
||||
|
||||
executeAutoSelectionOfBankAccount(linkedAccounts = postProcessedLinkedAccounts)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun postProcessLinkedAccounts(
|
||||
linkedAccounts: List<LinkedAccountEntity>
|
||||
): List<LinkedAccountEntity> {
|
||||
|
||||
// Account eligibility status update
|
||||
val updatedLinkedAccountsWithEligibility =
|
||||
updateAccountEligibilityStatusInLinkedAccounts(linkedAccounts = linkedAccounts)
|
||||
|
||||
return updatedLinkedAccountsWithEligibility
|
||||
}
|
||||
|
||||
private suspend fun updateAccountEligibilityStatusInLinkedAccounts(
|
||||
linkedAccounts: List<LinkedAccountEntity>
|
||||
): List<LinkedAccountEntity> {
|
||||
val updatedLinkedAccounts =
|
||||
linkedAccounts.map { linkedAccount ->
|
||||
val eligibilityState =
|
||||
getEligibilityStateForLinkedAccount(linkedAccountEntity = linkedAccount)
|
||||
linkedAccount.eligibilityState = eligibilityState
|
||||
linkedAccount
|
||||
}
|
||||
|
||||
return updatedLinkedAccounts
|
||||
}
|
||||
|
||||
private suspend fun getEligibilityStateForLinkedAccount(
|
||||
linkedAccountEntity: LinkedAccountEntity
|
||||
): EligibilityState {
|
||||
|
||||
// PIN set check
|
||||
if (!linkedAccountEntity.isMPinSet) {
|
||||
return EligibilityState(
|
||||
isAccountEligible = false,
|
||||
inEligibilityReasonResId = R.string.mpin_not_set
|
||||
)
|
||||
}
|
||||
|
||||
// Credit account check
|
||||
if (linkedAccountEntity.accountType == AccountType.CREDIT.name) {
|
||||
return EligibilityState(
|
||||
isAccountEligible = false,
|
||||
inEligibilityReasonResId =
|
||||
R.string.credit_cards_can_only_be_used_for_merchant_payments
|
||||
)
|
||||
}
|
||||
|
||||
// For create mandate, all accounts are eligible
|
||||
return if (isPendingMandateOfTypeCreate) {
|
||||
EligibilityState(isAccountEligible = true)
|
||||
} else { // For approve mandate, only payer VPA account is eligible
|
||||
EligibilityState(isAccountEligible = linkedAccountEntity.vpa == mandateEntity?.payerVpa)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun executeAutoSelectionOfBankAccount(
|
||||
linkedAccounts: List<LinkedAccountEntity>
|
||||
) {
|
||||
|
||||
val defaultSelectedAccount =
|
||||
linkedAccounts.firstOrNull { linkedAccount ->
|
||||
linkedAccount.vpa == mandateEntity?.payerVpa
|
||||
} ?: linkedAccounts.first()
|
||||
|
||||
updateSelectedBankAccount(selectedBankAccount = defaultSelectedAccount)
|
||||
}
|
||||
|
||||
private suspend fun validateVpaAndUpdateIsMerchantStateValues() {
|
||||
|
||||
val validateVpaAPIResponse =
|
||||
@@ -264,11 +299,11 @@ constructor(
|
||||
fun updateSelectedBankAccount(selectedBankAccount: LinkedAccountEntity) {
|
||||
_selectedBankAccount.update { selectedBankAccount }
|
||||
|
||||
updateShowAllBankListState(showAllState = false)
|
||||
updateAllBankListView(showAllBankList = false)
|
||||
}
|
||||
|
||||
fun updateShowAllBankListState(showAllState: Boolean) {
|
||||
_showAllBankList.update { showAllState }
|
||||
fun updateAllBankListView(showAllBankList: Boolean) {
|
||||
_showAllABankList.update { showAllBankList }
|
||||
}
|
||||
|
||||
fun updateIsChecked(isChecked: Boolean) {
|
||||
@@ -896,9 +931,9 @@ sealed class MandateDetailOfPendingCategoryBottomSheetState {
|
||||
val descriptionId: Int = R.string.loading_bottom_sheet_description
|
||||
) : MandateDetailOfPendingCategoryBottomSheetState()
|
||||
|
||||
object OptionsMenu : MandateDetailOfPendingCategoryBottomSheetState()
|
||||
data object OptionsMenu : MandateDetailOfPendingCategoryBottomSheetState()
|
||||
|
||||
object DeclineRequest : MandateDetailOfPendingCategoryBottomSheetState()
|
||||
data object DeclineRequest : MandateDetailOfPendingCategoryBottomSheetState()
|
||||
|
||||
data class Error(
|
||||
val title: String,
|
||||
@@ -910,11 +945,11 @@ sealed class MandateDetailOfPendingCategoryBottomSheetState {
|
||||
data class BlockSpamConfirmation(val reportSpam: Boolean) :
|
||||
MandateDetailOfPendingCategoryBottomSheetState()
|
||||
|
||||
object BlockLoader : MandateDetailOfPendingCategoryBottomSheetState()
|
||||
data object BlockLoader : MandateDetailOfPendingCategoryBottomSheetState()
|
||||
}
|
||||
|
||||
sealed class NavigateToNextScreenFromMandateDetailOfPendingCategory {
|
||||
object Back : NavigateToNextScreenFromMandateDetailOfPendingCategory()
|
||||
data object Back : NavigateToNextScreenFromMandateDetailOfPendingCategory()
|
||||
|
||||
data class MandateSummary(val mandateSummaryEntity: MandateSummaryEntity) :
|
||||
NavigateToNextScreenFromMandateDetailOfPendingCategory()
|
||||
|
||||
Reference in New Issue
Block a user