NTP-59175 | Divyesh | Bill history reminders enhancements (#16040)

This commit is contained in:
Divyesh Shinde
2025-05-21 15:17:30 +05:30
committed by GitHub
parent a0aea3f45d
commit aeecb082fa
15 changed files with 692 additions and 161 deletions

View File

@@ -25,6 +25,7 @@ import androidx.compose.ui.platform.SoftwareKeyboardController
import androidx.core.content.getSystemService
import com.navi.base.utils.orFalse
import com.navi.bbps.common.CATEGORY_ID_CREDIT_CARD
import com.navi.bbps.common.CATEGORY_ID_MOBILE_PREPAID
import com.navi.bbps.common.theme.NaviBbpsColor
import com.navi.bbps.feature.mybills.model.view.MyBillEntity
import com.navi.common.utils.ClickDebounce
@@ -104,6 +105,11 @@ fun MyBillEntity.isRedirectToCustomerInputRequired(): Boolean {
return this.categoryId == CATEGORY_ID_CREDIT_CARD && !isConsentProvided()
}
fun MyBillEntity.isPrepaidRechargePaidOrWithoutPlanDetails(): Boolean {
return categoryId == CATEGORY_ID_MOBILE_PREPAID &&
(isBillPaid || unpaidBillDetails?.planItemDetails.isNullOrEmpty())
}
fun getBillTitleFromAccountHolderNameOrPrimaryCustomerParams(
accountHolderName: String?,
primaryCustomerParams: String,

View File

@@ -172,6 +172,9 @@ constructor(
suspend fun fetchSavedBillsByCategory(category: String) =
myBillsDao.getBillsByCategory(categoryId = category)
fun fetchSavedBillsByCategoryAsFlow(categoryId: String) =
myBillsDao.getBillsByCategoryAsFlow(categoryId = categoryId)
suspend fun getRewardDetailsV2(
metricInfo: MetricInfo<RepoResult<RewardDetailsV2Response>>
): RepoResult<RewardDetailsV2Response> {

View File

@@ -70,4 +70,5 @@ object NaviBbpsColor {
val lightGreen = Color(0xFFF4FFF8)
val bgDarkPurple = Color(0xFF22223D)
val bottomSheetScrimColor = bgDarkPurple.copy(alpha = 0.64f)
val secondaryCtaDisabledTextColor = Color(0xFFAAAAAA)
}

View File

@@ -86,6 +86,7 @@ fun ThemeRoundedButton(
fun SecondaryRoundedButton(
modifier: Modifier = Modifier,
text: String,
enabled: Boolean = true,
cornerRadius: Dp = 4.dp,
paddingValues: PaddingValues = PaddingValues(vertical = 14.dp, horizontal = 10.dp),
onClick: () -> Unit,
@@ -99,13 +100,19 @@ fun SecondaryRoundedButton(
shape = RoundedCornerShape(cornerRadius),
colors = ButtonDefaults.buttonColors(backgroundColor = NaviBbpsColor.ctaSecondary),
contentPadding = paddingValues,
enabled = enabled,
) {
NaviText(
text = text,
fontSize = 14.sp,
fontFamily = naviFontFamily,
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_DEMI_BOLD),
color = NaviBbpsColor.ctaPrimary,
color =
if (enabled) {
NaviBbpsColor.ctaPrimary
} else {
NaviBbpsColor.secondaryCtaDisabledTextColor
},
)
}
}

View File

@@ -206,6 +206,7 @@ constructor(
initConfig()
initSearchFlow()
updateRewardsNudgeEntity()
observeRecentBills()
initLocationEvents()
}
@@ -604,6 +605,32 @@ constructor(
)
}
private fun observeRecentBills() {
viewModelScope.launch(dispatcherProvider.io) {
bbpsCommonRepository
.fetchSavedBillsByCategoryAsFlow(categoryId = billCategoryEntity.categoryId)
.distinctUntilChanged()
.collectLatest { recentBills ->
recentBillsEntity =
RecentBillsEntity(
title = resourceProvider.getString(R.string.bbps_recents),
bills = recentBills,
)
if (billerListState.value is BillerListState.Loaded) {
updateBillerListState(
BillerListState.Loaded(
recentBills = recentBillsEntity,
billerGroups =
(billerListState.value as BillerListState.Loaded).billerGroups,
popularBillers =
(billerListState.value as BillerListState.Loaded).popularBillers,
)
)
}
}
}
}
private fun createLoadedStateFromBillerResponse(
billerListResponse: RepoResult<BillerListResponse>
): BillerListState.Loaded {

View File

@@ -12,23 +12,29 @@ import androidx.lifecycle.viewModelScope
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.navi.base.model.CtaData
import com.navi.base.utils.DateUtils.getDateTimeObjectFromEpoch
import com.navi.base.utils.EMPTY
import com.navi.base.utils.NaviDateFormatter
import com.navi.base.utils.NaviNetworkConnectivity
import com.navi.base.utils.ResourceProvider
import com.navi.base.utils.SPACE
import com.navi.base.utils.retry
import com.navi.bbps.R
import com.navi.bbps.common.CATEGORY_ID_MOBILE_PREPAID
import com.navi.bbps.common.DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR
import com.navi.bbps.common.DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR_COMMA_TIME
import com.navi.bbps.common.DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR_INPUT
import com.navi.bbps.common.DEFAULT_RETRY_COUNT
import com.navi.bbps.common.DISPLAYABLE_MOBILE_NUMBER_KEY
import com.navi.bbps.common.NaviBbpsAnalytics
import com.navi.bbps.common.NaviBbpsScreen
import com.navi.bbps.common.RETRY_INTERVAL_IN_SECONDS
import com.navi.bbps.common.model.NaviBbpsVmData
import com.navi.bbps.common.model.config.NaviBbpsDefaultConfig
import com.navi.bbps.common.repository.BbpsCommonRepository
import com.navi.bbps.common.usecase.NaviBbpsConfigUseCase
import com.navi.bbps.common.utils.NaviBbpsCommonUtils
import com.navi.bbps.common.utils.NaviBbpsCommonUtils.getBbpsMetricInfo
import com.navi.bbps.common.utils.NaviBbpsCommonUtils.isRechargeCategory
import com.navi.bbps.common.utils.NaviBbpsDateUtils
import com.navi.bbps.common.utils.getDefaultConfig
import com.navi.bbps.common.utils.getDisplayableAmount
@@ -36,6 +42,7 @@ import com.navi.bbps.common.utils.toBillerAdditionalParamsEntity
import com.navi.bbps.common.viewmodel.NaviBbpsBaseVM
import com.navi.bbps.feature.billerlist.model.view.BillerItemEntity
import com.navi.bbps.feature.billhistorydetail.model.network.TransactionItemResponse
import com.navi.bbps.feature.billhistorydetail.model.view.AmountInfoData
import com.navi.bbps.feature.billhistorydetail.model.view.BillTransactionItemEntity
import com.navi.bbps.feature.billhistorydetail.model.view.BillTransactionsState
import com.navi.bbps.feature.billhistorydetail.model.view.MyBillDetailsBottomSheetType
@@ -54,23 +61,34 @@ import com.navi.bbps.feature.destinations.BbpsTransactionDetailsScreenDestinatio
import com.navi.bbps.feature.destinations.CustomerDataInputScreenDestination
import com.navi.bbps.feature.destinations.PayBillScreenDestination
import com.navi.bbps.feature.destinations.PrepaidRechargeScreenDestination
import com.navi.bbps.feature.mybills.MyBillsRepository
import com.navi.bbps.feature.mybills.MyBillsSyncJob
import com.navi.bbps.feature.mybills.MyBillsViewModel.SnackBarState
import com.navi.bbps.feature.mybills.model.view.MyBillEntity
import com.navi.bbps.feature.paybill.model.view.PayBillSource
import com.navi.bbps.feature.prepaidrecharge.model.view.OperatorItemEntity
import com.navi.bbps.feature.prepaidrecharge.model.view.PlanItemEntity
import com.navi.bbps.isPrepaidRechargePaidOrWithoutPlanDetails
import com.navi.bbps.isRedirectToCustomerInputRequired
import com.navi.common.di.CoroutineDispatcherProvider
import com.navi.common.extensions.or
import com.navi.common.network.models.RepoResult
import com.navi.common.network.models.isSuccessWithData
import com.navi.common.upi.AMOUNT
import com.navi.pay.R as NaviPayR
import com.ramcosta.composedestinations.spec.Direction
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
@@ -91,12 +109,17 @@ constructor(
private val stringResouceProvider: ResourceProvider,
private val naviBbpsConfigUseCase: NaviBbpsConfigUseCase,
private val resourceProvider: ResourceProvider,
private val myBillsRepository: MyBillsRepository,
private val myBillsSyncJob: MyBillsSyncJob,
) :
NaviBbpsBaseVM(
naviBbpsVmData = NaviBbpsVmData(screen = NaviBbpsScreen.NAVI_BBPS_MY_BILL_HISTORY_DETAILS)
) {
private val myBillEntity = savedStateHandle.get<MyBillEntity>("myBillEntity")!!
private val _myBillEntity =
MutableStateFlow(savedStateHandle.get<MyBillEntity>("myBillEntity")!!)
val myBillEntity = _myBillEntity.asStateFlow()
private val source = savedStateHandle.get<String>("source").orEmpty()
@@ -124,6 +147,9 @@ constructor(
private val _isLoading = MutableStateFlow(true)
val isLoading = _isLoading.asStateFlow()
private val _snackBarState = MutableStateFlow(SnackBarState(show = false))
val snackBarState = _snackBarState.asStateFlow()
private var naviBbpsDefaultConfig = NaviBbpsDefaultConfig()
private val naviBbpsAnalytics: NaviBbpsAnalytics.MyBillHistoryDetails =
@@ -165,22 +191,37 @@ constructor(
naviBbpsConfigUseCase.getDefaultConfig(naviBbpsVmData.screen.screenName)
}
}
updateSavedBillAsFlow()
}
private fun updateSavedBillAsFlow() {
viewModelScope.launch(dispatcherProvider.io) {
myBillsRepository
.fetchSavedBillsByCategoryAsFlow(myBillEntity.value.categoryId)
.distinctUntilChanged()
.collectLatest {
val savedBill = it.find { it.billId == myBillEntity.value.billId }
savedBill?.let { _myBillEntity.update { savedBill } }
}
}
}
fun getDisplayableCustomerParams(): Map<String, String> {
return if (myBillEntity.categoryId.equals(CATEGORY_ID_MOBILE_PREPAID, ignoreCase = true)) {
mapOf(DISPLAYABLE_MOBILE_NUMBER_KEY to myBillEntity.primaryCustomerParamValue)
return if (
myBillEntity.value.categoryId.equals(CATEGORY_ID_MOBILE_PREPAID, ignoreCase = true)
) {
mapOf(DISPLAYABLE_MOBILE_NUMBER_KEY to myBillEntity.value.primaryCustomerParamValue)
} else {
myBillEntity.customerParams
myBillEntity.value.customerParams
}
}
fun fetchBillHistoryList() {
viewModelScope.launch(dispatcherProvider.io) {
if (myBillEntity.billId.isNullOrEmpty()) {
if (myBillEntity.value.billId.isNullOrEmpty()) {
_billHistoryDetailsState.update {
BillTransactionsState.createEmptyState(
categoryId = myBillEntity.categoryId,
categoryId = myBillEntity.value.categoryId,
billHistoryEmptyStateConfig =
naviBbpsDefaultConfig.billHistoryEmptyStateConfig,
)
@@ -189,7 +230,7 @@ constructor(
}
val billTransactionsResponse =
billHistoryDetailsRepository.fetchBillTransactions(
savedBillId = myBillEntity.billId,
savedBillId = myBillEntity.value.billId,
metricInfo = getBbpsMetricInfo(screenName = naviBbpsVmData.screen.screenName),
)
if (billTransactionsResponse.isSuccessWithData()) {
@@ -197,7 +238,7 @@ constructor(
_billHistoryDetailsState.update {
if (transactions.isEmpty()) {
BillTransactionsState.createEmptyState(
categoryId = myBillEntity.categoryId,
categoryId = myBillEntity.value.categoryId,
billHistoryEmptyStateConfig =
naviBbpsDefaultConfig.billHistoryEmptyStateConfig,
)
@@ -209,7 +250,7 @@ constructor(
}
contactNameForMobileNumber =
getCustomerNameForMobileNumber(
primaryCustomerParamValue = myBillEntity.primaryCustomerParamValue
primaryCustomerParamValue = myBillEntity.value.primaryCustomerParamValue
)
} else {
_billHistoryDetailsState.update { BillTransactionsState.Error }
@@ -258,7 +299,7 @@ constructor(
formattedAmount = amount.getDisplayableAmount(),
netAmountAfterDiscount = "",
planName = planName ?: "",
primaryCustomerParamValue = myBillEntity.primaryCustomerParamValue,
primaryCustomerParamValue = myBillEntity.value.primaryCustomerParamValue,
billDate =
if (billDate.isNullOrBlank()) ""
else
@@ -289,13 +330,17 @@ constructor(
_myBillDetailsBottomSheetType.update { MyBillDetailsBottomSheetType.MenuOptions }
}
fun updateSnackBarState(show: Boolean, messageId: Int = R.string.bbps_copied_to_clipboard) {
_snackBarState.update { SnackBarState(show = show, messageId = messageId) }
}
fun onTransactionItemClicked(billTransactionItemEntity: BillTransactionItemEntity) {
viewModelScope.launch(dispatcherProvider.io) {
updateNextScreenDestinationState(
BbpsTransactionDetailsScreenDestination(
billTransactionItemEntity = billTransactionItemEntity,
myBillEntity =
myBillEntity.copy(customerParams = getDisplayableCustomerParams()),
myBillEntity.value.copy(customerParams = getDisplayableCustomerParams()),
source = NaviBbpsScreen.NAVI_BBPS_MY_BILL_HISTORY_DETAILS.name,
initialSource = initialSource,
)
@@ -303,6 +348,61 @@ constructor(
}
}
fun getNextActionFooterCtaText(): String {
val billState = myBillEntity.value
val categoryId = billState.categoryId
return when {
billState.isBillPaid -> {
if (categoryId == CATEGORY_ID_MOBILE_PREPAID) {
resourceProvider.getString(R.string.bbps_view_all_plans)
} else {
resourceProvider.getString(R.string.bbps_pay_bill_in_advance)
}
}
billState.nextActionCtaText.isNotBlank() -> {
billState.nextActionCtaText
}
isRechargeCategory(categoryId) -> {
resourceProvider.getString(R.string.bbps_recharge)
}
else -> {
resourceProvider.getString(R.string.bbps_pay_bill_in_advance)
}
}
}
fun getAmountInfoFooterText(): AmountInfoData? {
val isRechargeCategory = isRechargeCategory(categoryId = myBillEntity.value.categoryId)
return when {
isRechargeCategory && !myBillEntity.value.formattedLastPaidAmount.isBlank() -> {
AmountInfoData(
amountPrefixText =
resourceProvider.getString(resId = R.string.bbps_last_recharged, AMOUNT),
amount = myBillEntity.value.formattedLastPaidAmount,
)
}
!isRechargeCategory &&
!myBillEntity.value.unpaidBillDetails?.amount.isNullOrEmpty() -> {
AmountInfoData(
amountPrefixText =
myBillEntity.value.categoryName +
SPACE +
resourceProvider.getString(resId = R.string.bbps_bill_amount),
amount =
myBillEntity.value.unpaidBillDetails
?.amount
?.getDisplayableAmount()
.orEmpty(),
)
}
else -> null
}
}
fun onDeleteBillMenuClicked(deleteBill: () -> Unit) {
_myBillDetailsBottomSheetType.update {
MyBillDetailsBottomSheetType.Confirmation(
@@ -316,6 +416,55 @@ constructor(
}
}
fun onMarkAsPaidClicked() {
_myBillDetailsBottomSheetType.update {
MyBillDetailsBottomSheetType.Confirmation(
title = resourceProvider.getString(R.string.bbps_bill_mark_as_paid_heading),
description =
resourceProvider.getString(R.string.bbps_bill_mark_as_paid_description),
firstBtnTextResId = R.string.bbps_no,
secondButtonTextResId = R.string.bbps_yes,
onFirstBtnClick = {},
onSecondBtnClick = { onMarkBillAsPaidConfirmedCtaClicked(myBillEntity.value) },
)
}
}
private fun onMarkBillAsPaidConfirmedCtaClicked(myBillEntity: MyBillEntity) {
viewModelScope.launch(dispatcherProvider.io) {
delay(50) // before this bottom sheet is getting closed, added for smoother
// transition
if (!naviNetworkConnectivity.isInternetConnected()) {
notifyError(getNoInternetErrorConfig())
return@launch
}
val response =
retry(
retryCount = DEFAULT_RETRY_COUNT,
retryIntervalInSeconds = RETRY_INTERVAL_IN_SECONDS,
execute = {
myBillsRepository.markBillAsPaid(
myBillEntity.billId,
metricInfo =
getBbpsMetricInfo(
screenName = naviBbpsVmData.screen.screenName,
isNae = { false },
),
)
},
shouldRetry = { !it.isSuccessWithData() },
)
if (response.isSuccessWithData()) {
updateSnackBarState(show = true, messageId = R.string.bbps_bill_marked_as_paid)
_myBillEntity.update { it.copy(isBillPaid = true) }
myBillsSyncJob.refreshBillsAsync(screenName = naviBbpsVmData.screen.screenName)
billResponse = null
}
}
}
private fun updateNewBillFetchingState(isInProgress: Boolean) {
_isFetchingNewBill.update { isInProgress }
}
@@ -326,8 +475,8 @@ constructor(
) {
val billCategoryEntity =
BillCategoryEntity(
categoryId = myBillEntity.categoryId,
title = myBillEntity.categoryName,
categoryId = myBillEntity.value.categoryId,
title = myBillEntity.value.categoryName,
iconUrl = "",
searchBoxPlaceholderText = "",
billerListStateHeading = "",
@@ -361,39 +510,101 @@ constructor(
billPeriod = billDetails.billPeriod.orEmpty(),
)
val payBillSource =
PayBillSource.Others(
billerDetailsEntity = billerDetails,
billDetailsEntity = billDetailsEntity,
customerParams = myBillEntity.customerParams,
billerId = myBillEntity.billerId,
amount = billDetails.amount,
formattedLastPaidDate = myBillEntity.formattedLastPaidDate,
formattedLastPaidAmount = myBillEntity.formattedLastPaidAmount,
isConsentRequired = myBillEntity.isConsentRequired,
)
val phoneNumberDetail =
PhoneContactEntity(
name = myBillEntity.billerName,
phoneNumber = myBillEntity.primaryCustomerParamValue,
normalisedPhoneNumber = myBillEntity.primaryCustomerParamValue,
name = myBillEntity.value.billerName,
phoneNumber = myBillEntity.value.primaryCustomerParamValue,
normalisedPhoneNumber = myBillEntity.value.primaryCustomerParamValue,
)
val source = NaviBbpsScreen.NAVI_BBPS_MY_BILL_HISTORY_DETAILS.name
if (billCategoryEntity.categoryId == CATEGORY_ID_MOBILE_PREPAID) {
val planItemEntity =
PlanItemEntity(
planName =
myBillEntity.value.unpaidBillDetails
?.planItemDetails
?.get("planName")
.orEmpty(),
price =
myBillEntity.value.unpaidBillDetails
?.planItemDetails
?.get("price")
.orEmpty(),
validity =
myBillEntity.value.unpaidBillDetails
?.planItemDetails
?.get("validity")
.orEmpty(),
talkTime =
myBillEntity.value.unpaidBillDetails
?.planItemDetails
?.get("talkTime")
.orEmpty(),
packageDescription =
myBillEntity.value.unpaidBillDetails
?.planItemDetails
?.get("packageDescription")
.orEmpty(),
)
val destination =
PayBillScreenDestination(
billCategoryEntity = billCategoryEntity,
payBillScreenSource = payBillSource,
phoneNumberDetail = phoneNumberDetail,
isRootScreen = isRootScreen,
source = source,
initialSource = initialSource,
isBillSyncRequiredInBackground = false,
val operatorItemEntity =
OperatorItemEntity(
operatorLogoUrl = billerDetails.billerLogoUrl,
operatorCode = EMPTY,
operatorName = myBillEntity.value.billerName,
billerId = billerDetails.billerId,
)
val payBillSource =
PayBillSource.Prepaid(
planItemEntity = planItemEntity,
billDetailsEntity = billDetailsEntity,
operatorItemEntity = operatorItemEntity,
customerParams = myBillEntity.value.customerParams,
billerId = billerDetails.billerId,
amount = billDetailsEntity.amount.orEmpty(),
formattedLastPaidDate = myBillEntity.value.formattedLastPaidDate,
formattedLastPaidAmount = myBillEntity.value.formattedLastPaidAmount,
)
_navigateToNextScreen.emit(
PayBillScreenDestination(
billCategoryEntity = billCategoryEntity,
payBillScreenSource = payBillSource,
phoneNumberDetail = phoneNumberDetail,
isRootScreen = isRootScreen,
source = source,
initialSource = initialSource,
)
)
} else {
val payBillSource =
PayBillSource.Others(
billerDetailsEntity = billerDetails,
billDetailsEntity = billDetailsEntity,
customerParams = myBillEntity.value.customerParams,
billerId = myBillEntity.value.billerId,
amount = billDetails.amount,
formattedLastPaidDate = myBillEntity.value.formattedLastPaidDate,
formattedLastPaidAmount = myBillEntity.value.formattedLastPaidAmount,
isConsentRequired = myBillEntity.value.isConsentRequired,
)
_navigateToNextScreen.emit(destination)
val source = NaviBbpsScreen.NAVI_BBPS_MY_BILL_HISTORY_DETAILS.name
val destination =
PayBillScreenDestination(
billCategoryEntity = billCategoryEntity,
payBillScreenSource = payBillSource,
phoneNumberDetail = phoneNumberDetail,
isRootScreen = isRootScreen,
source = source,
initialSource = initialSource,
isBillSyncRequiredInBackground = false,
)
_navigateToNextScreen.emit(destination)
}
}
private suspend fun updateNextScreenDestinationState(direction: Direction) {
@@ -402,19 +613,19 @@ constructor(
fun onPayNowClicked() {
viewModelScope.launch(Dispatchers.IO) {
if (myBillEntity.categoryId == CATEGORY_ID_MOBILE_PREPAID) {
if (myBillEntity.value.isPrepaidRechargePaidOrWithoutPlanDetails()) {
updateNextScreenDestinationState(
PrepaidRechargeScreenDestination(
phoneNumberDetail =
PhoneContactEntity(
name = contactNameForMobileNumber,
phoneNumber = myBillEntity.primaryCustomerParamValue,
normalisedPhoneNumber = myBillEntity.primaryCustomerParamValue,
phoneNumber = myBillEntity.value.primaryCustomerParamValue,
normalisedPhoneNumber = myBillEntity.value.primaryCustomerParamValue,
),
billCategoryEntity =
BillCategoryEntity(
categoryId = myBillEntity.categoryId,
title = myBillEntity.categoryName,
categoryId = myBillEntity.value.categoryId,
title = myBillEntity.value.categoryName,
iconUrl = "",
searchBoxPlaceholderText = "",
billerListStateHeading = "",
@@ -424,19 +635,19 @@ constructor(
initialSource = initialSource,
)
)
} else if (myBillEntity.isRedirectToCustomerInputRequired()) {
} else if (myBillEntity.value.isRedirectToCustomerInputRequired()) {
goToCustomerDataInputScreen()
} else {
updateNewBillFetchingState(isInProgress = true)
val billerDetails =
BillerDetailsEntity(
billerId = myBillEntity.billerId,
billerName = myBillEntity.billerName,
billerLogoUrl = myBillEntity.billerLogoUrl,
status = myBillEntity.status,
isAdhoc = myBillEntity.isAdhoc,
fetchOption = myBillEntity.fetchOption,
paymentAmountExactness = myBillEntity.paymentAmountExactness,
billerId = myBillEntity.value.billerId,
billerName = myBillEntity.value.billerName,
billerLogoUrl = myBillEntity.value.billerLogoUrl,
status = myBillEntity.value.status,
isAdhoc = myBillEntity.value.isAdhoc,
fetchOption = myBillEntity.value.fetchOption,
paymentAmountExactness = myBillEntity.value.paymentAmountExactness,
)
if (billResponse?.isSuccessWithData() == true) {
@@ -458,24 +669,24 @@ constructor(
CustomerDataInputScreenDestination(
billerItemEntity =
BillerItemEntity(
billerId = myBillEntity.billerId,
billerName = myBillEntity.billerName,
billerLogoUrl = myBillEntity.billerLogoUrl,
status = myBillEntity.status,
isAdhoc = myBillEntity.isAdhoc,
billerId = myBillEntity.value.billerId,
billerName = myBillEntity.value.billerName,
billerLogoUrl = myBillEntity.value.billerLogoUrl,
status = myBillEntity.value.status,
isAdhoc = myBillEntity.value.isAdhoc,
state = "",
region = "",
categoryId = myBillEntity.categoryId,
categoryId = myBillEntity.value.categoryId,
isPopular = false,
),
customerParamsExtraData =
CustomerParamsExtraData(
existingCustomerParams = myBillEntity.customerParams
existingCustomerParams = myBillEntity.value.customerParams
),
billCategoryEntity =
BillCategoryEntity(
categoryId = myBillEntity.categoryId,
title = myBillEntity.categoryName,
categoryId = myBillEntity.value.categoryId,
title = myBillEntity.value.categoryName,
iconUrl = "",
searchBoxPlaceholderText = "",
billerListStateHeading = "",
@@ -490,23 +701,23 @@ constructor(
}
private suspend fun fetchBillDetailsResponse(): RepoResult<BillDetailsResponse> {
naviBbpsAnalytics.onLoaded(myBillEntity = myBillEntity)
naviBbpsAnalytics.onLoaded(myBillEntity = myBillEntity.value)
val billerDetails =
BillerDetailsEntity(
billerId = myBillEntity.billerId,
billerName = myBillEntity.billerName,
billerLogoUrl = myBillEntity.billerLogoUrl,
status = myBillEntity.status,
isAdhoc = myBillEntity.isAdhoc,
fetchOption = myBillEntity.fetchOption,
paymentAmountExactness = myBillEntity.paymentAmountExactness,
billerId = myBillEntity.value.billerId,
billerName = myBillEntity.value.billerName,
billerLogoUrl = myBillEntity.value.billerLogoUrl,
status = myBillEntity.value.status,
isAdhoc = myBillEntity.value.isAdhoc,
fetchOption = myBillEntity.value.fetchOption,
paymentAmountExactness = myBillEntity.value.paymentAmountExactness,
)
val billDetailsRequest =
BillDetailsRequest(
billerId = billerDetails.billerId,
customerParams = myBillEntity.customerParams,
customerParams = myBillEntity.value.customerParams,
deviceDetails = DeviceDetails(ip = naviNetworkConnectivity.getIpAddress()),
isConsentProvided = false,
)

View File

@@ -0,0 +1,10 @@
/*
*
* * Copyright © 2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.bbps.feature.billhistorydetail.model.view
data class AmountInfoData(val amountPrefixText: String, val amount: String)

View File

@@ -10,6 +10,8 @@ package com.navi.bbps.feature.billhistorydetail.ui
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -19,6 +21,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.rememberModalBottomSheetState
import androidx.compose.material3.Scaffold
@@ -30,6 +33,8 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -41,6 +46,7 @@ import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.navi.base.utils.EMPTY
import com.navi.base.utils.SPACE
import com.navi.base.utils.orTrue
import com.navi.bbps.R
import com.navi.bbps.common.BULLET
import com.navi.bbps.common.NaviBbpsAnalytics
@@ -51,12 +57,15 @@ import com.navi.bbps.common.ui.BbpsCircleImage
import com.navi.bbps.common.ui.DisplayAccountHolderNameAndPrimaryCustomerParamValue
import com.navi.bbps.common.ui.NaviBbpsHeader
import com.navi.bbps.common.ui.NaviBbpsModalBottomSheetLayout
import com.navi.bbps.common.ui.SecondaryRoundedButton
import com.navi.bbps.common.ui.SetStatusBarColor
import com.navi.bbps.common.utils.NaviBbpsCommonUtils.isRechargeCategory
import com.navi.bbps.common.utils.SnackBarPredefinedConfig
import com.navi.bbps.entry.NaviBbpsActivity
import com.navi.bbps.entry.NaviBbpsRouter
import com.navi.bbps.feature.billhistorydetail.BillHistoryDetailsViewModel
import com.navi.bbps.feature.billhistorydetail.model.view.AmountInfoData
import com.navi.bbps.feature.billhistorydetail.model.view.BillTransactionItemEntity
import com.navi.bbps.feature.billhistorydetail.model.view.BillTransactionsState
import com.navi.bbps.feature.mybills.model.view.MyBillEntity
import com.navi.common.R as CommonR
import com.navi.common.customview.LoaderRoundedButton
@@ -65,8 +74,10 @@ import com.navi.common.navigation.setResultAndNavigateBack
import com.navi.design.font.FontWeightEnum
import com.navi.design.font.getFontWeight
import com.navi.design.font.naviFontFamily
import com.navi.design.snackbar.SuccessSnackBar
import com.navi.design.utils.clickableWithNoGesture
import com.navi.naviwidgets.extensions.NaviText
import com.navi.payment.nativepayment.components.BottomBarShadow
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.result.ResultBackNavigator
@@ -139,6 +150,8 @@ fun MyBillHistoryDetailsScreen(
billHistoryDetailsViewModel.myBillDetailsBottomSheetType.collectAsStateWithLifecycle()
val isUserActionBlocked by
billHistoryDetailsViewModel.isUserActionBlocked.collectAsStateWithLifecycle()
val snackBarState by billHistoryDetailsViewModel.snackBarState.collectAsStateWithLifecycle()
val myBillEntityState by billHistoryDetailsViewModel.myBillEntity.collectAsStateWithLifecycle()
val onTransactionItemClicked = { billTransactionItemEntity: BillTransactionItemEntity ->
naviBbpsAnalytics.onTransactionItemClicked(
@@ -207,12 +220,14 @@ fun MyBillHistoryDetailsScreen(
myBillDetailsBottomSheetType = myBillDetailsBottomSheetType,
onDeleteBillMenuClicked = {
naviBbpsAnalytics.onDeleteMenuClicked(
myBillEntity = myBillEntity,
myBillEntity = myBillEntityState,
source = source,
initialSource = initialSource,
)
billHistoryDetailsViewModel.onDeleteBillMenuClicked {
naviBbpsActivity.navController.setResultAndNavigateBack(myBillEntity.billId)
naviBbpsActivity.navController.setResultAndNavigateBack(
myBillEntityState.billId
)
}
openSheet()
naviBbpsAnalytics.onBottomSheetLanded(
@@ -233,11 +248,11 @@ fun MyBillHistoryDetailsScreen(
topBar = {
MyBillsHistoryHeader(
navigator = navigator,
myBillEntity = myBillEntity,
myBillEntity = myBillEntityState,
isMenuButtonEnabled = isUserActionBlocked.not(),
onMoreOptionsClicked = {
naviBbpsAnalytics.onKebabMenuClicked(
myBillEntity,
myBillEntityState,
source = source,
initialSource = initialSource,
)
@@ -255,58 +270,72 @@ fun MyBillHistoryDetailsScreen(
)
},
content = { innerPadding ->
Column(
modifier =
Modifier.fillMaxSize()
.background(color = NaviBbpsColor.bgDefault)
.padding(innerPadding)
Box(
Modifier.padding(innerPadding).fillMaxSize().background(NaviBbpsColor.textWhite)
) {
BillTransactionsListView(
billHistoryDetailsState = billHistoryDetailsState,
myBillEntity = myBillEntity,
onItemClicked = onTransactionItemClicked,
recordScreenRenderTime = {
billHistoryDetailsViewModel.recordScreenRenderTime(
billHistoryDetailsViewModel.naviBbpsVmData.screen.screenName,
ModuleNameV2.BBPS.name,
)
Column(
modifier =
Modifier.fillMaxWidth().background(color = NaviBbpsColor.bgDefault)
) {
BillTransactionsListView(
billHistoryDetailsState = billHistoryDetailsState,
myBillEntity = myBillEntityState,
onItemClicked = onTransactionItemClicked,
recordScreenRenderTime = {
billHistoryDetailsViewModel.recordScreenRenderTime(
billHistoryDetailsViewModel.naviBbpsVmData.screen.screenName,
ModuleNameV2.BBPS.name,
)
},
recordApiEndTime = {
billHistoryDetailsViewModel.recordApiEndTime(
billHistoryDetailsViewModel.naviBbpsVmData.screen.screenName
)
},
getTransactionStatusDisplayText =
billHistoryDetailsViewModel::getTransactionStatusDisplayText,
)
}
BottomBarShadow(shadowHeight = 32.dp)
}
},
snackbarHost = {
if (snackBarState.show) {
SuccessSnackBar(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 32.dp),
show = true,
snackBarConfig =
SnackBarPredefinedConfig.successConfig(
title = stringResource(id = snackBarState.messageId),
trailingIconResId = null,
),
onDismissed = {
billHistoryDetailsViewModel.updateSnackBarState(show = false)
},
recordApiEndTime = {
billHistoryDetailsViewModel.recordApiEndTime(
billHistoryDetailsViewModel.naviBbpsVmData.screen.screenName
)
},
getTransactionStatusDisplayText =
billHistoryDetailsViewModel::getTransactionStatusDisplayText,
)
}
},
bottomBar = {
val nextCtaText =
when (billHistoryDetailsState) {
is BillTransactionsState.Empty ->
(billHistoryDetailsState as BillTransactionsState.Empty)
.emptyStateEntity
.ctaText
else -> myBillEntity.nextActionCtaText
}
LoaderRoundedButton(
text = nextCtaText,
onClick = {
naviBbpsAnalytics.onPayNowClicked(
myBillEntity,
source = source,
initialSource = initialSource,
)
billHistoryDetailsViewModel.onPayNowClicked()
},
enabled = !isUserActionBlocked,
showLoader = isUserActionBlocked,
modifier =
Modifier.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 32.dp)
.height(48.dp),
)
Column(modifier = Modifier.fillMaxWidth().background(NaviBbpsColor.textWhite)) {
MyBillsHistoryFooter(
myBillEntity = myBillEntityState,
onMarkAsPaidClicked = {
billHistoryDetailsViewModel.onMarkAsPaidClicked()
openSheet()
},
onPrimaryButtonClicked = {
naviBbpsAnalytics.onPayNowClicked(
myBillEntityState,
source = source,
initialSource = initialSource,
)
billHistoryDetailsViewModel.onPayNowClicked()
},
isUserActionBlocked = isUserActionBlocked,
nextCtaText = billHistoryDetailsViewModel.getNextActionFooterCtaText(),
amountInfoData = billHistoryDetailsViewModel.getAmountInfoFooterText(),
)
}
},
)
}
@@ -396,3 +425,146 @@ private fun MyBillsHistoryHeader(
}
}
}
@Composable
private fun MyBillsHistoryFooter(
myBillEntity: MyBillEntity?,
onMarkAsPaidClicked: () -> Unit,
onPrimaryButtonClicked: () -> Unit,
nextCtaText: String,
isUserActionBlocked: Boolean,
amountInfoData: AmountInfoData?,
) {
Column(
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
if (myBillEntity?.isBillPaid.orTrue()) {
if (!isRechargeCategory(categoryId = myBillEntity?.categoryId.orEmpty())) {
NaviText(
text = stringResource(id = R.string.bbps_no_bills_due_description),
fontSize = 12.sp,
lineHeight = 16.sp,
color = NaviBbpsColor.textTertiary,
fontFamily = naviFontFamily,
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
)
Spacer(modifier = Modifier.height(16.dp))
}
LoaderRoundedButton(
text = nextCtaText,
onClick = onPrimaryButtonClicked,
enabled = !isUserActionBlocked,
showLoader = isUserActionBlocked,
modifier = Modifier.fillMaxWidth().height(48.dp),
)
} else {
amountInfoData?.let { BillAmountInfo(amountInfoData = amountInfoData) }
if (!myBillEntity?.unpaidBillWarning.isNullOrEmpty()) {
Row(
modifier =
Modifier.fillMaxWidth()
.background(
color = NaviBbpsColor.bgError,
shape =
RoundedCornerShape(
topStart = 0.dp,
topEnd = 0.dp,
bottomStart = 4.dp,
bottomEnd = 4.dp,
),
)
.padding(vertical = 2.dp),
horizontalArrangement = Arrangement.Center,
) {
NaviText(
text = myBillEntity.unpaidBillWarning,
fontSize = 12.sp,
lineHeight = 18.sp,
fontFamily = naviFontFamily,
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
color = NaviBbpsColor.onSurfaceCritical,
)
}
}
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
SecondaryRoundedButton(
text = stringResource(id = R.string.bbps_mark_as_paid),
onClick = onMarkAsPaidClicked,
modifier = Modifier.weight(1f),
enabled = !isUserActionBlocked,
)
Spacer(modifier = Modifier.width(16.dp))
LoaderRoundedButton(
text = nextCtaText,
onClick = onPrimaryButtonClicked,
enabled = !isUserActionBlocked,
showLoader = isUserActionBlocked,
modifier = Modifier.weight(1f).height(48.dp),
)
}
}
}
}
@Composable
private fun BillAmountInfo(amountInfoData: AmountInfoData) {
Row(
modifier =
Modifier.fillMaxWidth()
.background(
color = NaviBbpsColor.bgAlt,
shape =
RoundedCornerShape(
topStart = 4.dp,
topEnd = 4.dp,
bottomStart = 0.dp,
bottomEnd = 0.dp,
),
)
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
NaviText(
text = amountInfoData.amountPrefixText,
fontSize = 14.sp,
lineHeight = 22.sp,
fontFamily = naviFontFamily,
color = NaviBbpsColor.textPrimary,
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
)
NaviText(
text = BULLET,
fontSize = 14.sp,
lineHeight = 22.sp,
fontFamily = naviFontFamily,
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_DEMI_BOLD),
color = NaviBbpsColor.textPrimary,
modifier = Modifier.padding(horizontal = 4.dp),
)
NaviText(
text = stringResource(id = R.string.bbps_rupee_symbol_x, amountInfoData.amount),
fontSize = 14.sp,
lineHeight = 22.sp,
fontFamily = naviFontFamily,
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_DEMI_BOLD),
color = NaviBbpsColor.textPrimary,
)
}
}

View File

@@ -83,6 +83,7 @@ import com.navi.bbps.feature.mybills.model.view.MyBillEntity
import com.navi.bbps.feature.paybill.model.view.PayBillSource
import com.navi.bbps.feature.prepaidrecharge.model.view.OperatorItemEntity
import com.navi.bbps.feature.prepaidrecharge.model.view.PlanItemEntity
import com.navi.bbps.isPrepaidRechargePaidOrWithoutPlanDetails
import com.navi.bbps.isRedirectToCustomerInputRequired
import com.navi.bbps.network.di.NaviBbpsGsonBuilder
import com.navi.common.constants.ARC_LOCAL_COUNTER_KEY
@@ -516,10 +517,7 @@ constructor(
myBillEntityToBillerDetailsEntityMapper.mapMyBillEntityToBillerDetailsEntity(
myBillEntity = myBillEntity
)
if (
myBillEntity.unpaidBillDetails?.planItemDetails.isNullOrEmpty() &&
myBillEntity.categoryId == CATEGORY_ID_MOBILE_PREPAID
) {
if (myBillEntity.isPrepaidRechargePaidOrWithoutPlanDetails()) {
goToPrepaidRechargeScreen(myBillEntity)
} else if (myBillEntity.isRedirectToCustomerInputRequired()) {
goToCustomerInputScreen(myBillEntity)

View File

@@ -56,6 +56,9 @@ import com.navi.bbps.feature.mybills.MyBillsSyncJob
import com.navi.bbps.feature.mybills.model.view.MyBillEntity
import com.navi.bbps.feature.paybill.model.network.PaymentAmountExactness
import com.navi.bbps.feature.paybill.model.view.PayBillSource
import com.navi.bbps.feature.prepaidrecharge.model.view.OperatorItemEntity
import com.navi.bbps.feature.prepaidrecharge.model.view.PlanItemEntity
import com.navi.bbps.isPrepaidRechargePaidOrWithoutPlanDetails
import com.navi.common.di.CoroutineDispatcherProvider
import com.navi.common.extensions.removeSpaces
import com.navi.common.model.common.NudgeDetailEntity
@@ -71,7 +74,9 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -195,6 +200,7 @@ constructor(
initConfig()
fetchUserPhoneNumber()
updateRewardsNudgeEntity()
observeRecentBills()
}
private fun initConfig() {
@@ -491,6 +497,30 @@ constructor(
}
}
private fun observeRecentBills() {
viewModelScope.launch(dispatcherProvider.io) {
bbpsCommonRepository
.fetchSavedBillsByCategoryAsFlow(categoryId = billCategoryEntity.categoryId)
.distinctUntilChanged()
.collectLatest { recentBills ->
if (contactListUIState.value is ContactListState.Loaded) {
updateContactListUIState(
ContactListState.Loaded(
recentBills =
RecentBillsEntity(
title = resourceProvider.getString(R.string.bbps_recents),
bills = recentBills,
),
allContactList =
(contactListUIState.value as ContactListState.Loaded)
.allContactList,
)
)
}
}
}
}
private suspend fun getRecentBills(): RecentBillsEntity {
val savedBills =
bbpsCommonRepository.fetchSavedBillsByCategory(category = billCategoryEntity.categoryId)
@@ -580,17 +610,6 @@ constructor(
billDetails = myBillEntityToBillDetailsResponseMapper.map(myBillEntity)
)
val payBillSource =
PayBillSource.Others(
billDetailsEntity = billDetailsEntity,
billerDetailsEntity = billerDetails,
customerParams = myBillEntity.customerParams,
formattedLastPaidDate = myBillEntity.formattedLastPaidDate,
formattedLastPaidAmount = myBillEntity.formattedLastPaidAmount,
billerId = billerDetails.billerId,
isConsentRequired = null,
)
val phoneNumber =
PhoneContactEntity(
name = phoneNumberDetail.name,
@@ -600,16 +619,83 @@ constructor(
val screenSource = NaviBbpsScreen.NAVI_BBPS_CONTACT_LIST_SCREEN.name
_navigateToNextScreen.emit(
PayBillScreenDestination(
billCategoryEntity = billCategory,
payBillScreenSource = payBillSource,
phoneNumberDetail = phoneNumber,
isRootScreen = isRootScreen,
source = screenSource,
initialSource = initialSource,
if (billCategory.categoryId == CATEGORY_ID_MOBILE_PREPAID && !myBillEntity.isBillPaid) {
val planItemEntity =
PlanItemEntity(
planName =
myBillEntity.unpaidBillDetails?.planItemDetails?.get("planName").orEmpty(),
price = myBillEntity.unpaidBillDetails?.planItemDetails?.get("price").orEmpty(),
validity =
myBillEntity.unpaidBillDetails?.planItemDetails?.get("validity").orEmpty(),
talkTime =
myBillEntity.unpaidBillDetails?.planItemDetails?.get("talkTime").orEmpty(),
packageDescription =
myBillEntity.unpaidBillDetails
?.planItemDetails
?.get("packageDescription")
.orEmpty(),
)
val operatorItemEntity =
OperatorItemEntity(
operatorLogoUrl = billerDetails.billerLogoUrl,
operatorCode = EMPTY,
operatorName = myBillEntity.billerName,
billerId = billerDetails.billerId,
)
val payBillSource =
PayBillSource.Prepaid(
planItemEntity = planItemEntity,
billDetailsEntity = billDetailsEntity!!,
operatorItemEntity = operatorItemEntity,
customerParams = myBillEntity.customerParams,
billerId = billerDetails.billerId,
amount = billDetailsEntity.amount.orEmpty(),
formattedLastPaidDate = myBillEntity.formattedLastPaidDate,
formattedLastPaidAmount = myBillEntity.formattedLastPaidAmount,
)
_navigateToNextScreen.emit(
PayBillScreenDestination(
billCategoryEntity = billCategory,
payBillScreenSource = payBillSource,
phoneNumberDetail = phoneNumber,
isRootScreen = isRootScreen,
source = screenSource,
initialSource = initialSource,
)
)
)
} else {
val payBillSource =
PayBillSource.Others(
billDetailsEntity = billDetailsEntity,
billerDetailsEntity = billerDetails,
customerParams = myBillEntity.customerParams,
formattedLastPaidDate = myBillEntity.formattedLastPaidDate,
formattedLastPaidAmount = myBillEntity.formattedLastPaidAmount,
billerId = billerDetails.billerId,
isConsentRequired = null,
)
val phoneNumber =
PhoneContactEntity(
name = phoneNumberDetail.name,
phoneNumber = phoneNumberDetail.phoneNumber,
normalisedPhoneNumber = phoneNumberDetail.normalisedPhoneNumber,
)
_navigateToNextScreen.emit(
PayBillScreenDestination(
billCategoryEntity = billCategory,
payBillScreenSource = payBillSource,
phoneNumberDetail = phoneNumber,
isRootScreen = isRootScreen,
source = screenSource,
initialSource = initialSource,
)
)
}
}
fun onRecentBillItemClicked(phoneNumberDetail: PhoneContactEntity, myBillEntity: MyBillEntity) {
@@ -621,7 +707,17 @@ constructor(
source = source,
initialSource = initialSource,
)
if (billCategoryEntity.categoryId == CATEGORY_ID_MOBILE_PREPAID) {
val billerDetails =
BillerDetailsEntity(
billerId = myBillEntity.billerId,
billerName = myBillEntity.billerName,
billerLogoUrl = myBillEntity.billerLogoUrl,
status = myBillEntity.status,
isAdhoc = myBillEntity.isAdhoc,
fetchOption = EMPTY,
paymentAmountExactness = myBillEntity.paymentAmountExactness,
)
if (myBillEntity.isPrepaidRechargePaidOrWithoutPlanDetails()) {
updateNextScreenDestinationState(
PrepaidRechargeScreenDestination(
phoneNumberDetail =
@@ -645,16 +741,6 @@ constructor(
)
)
} else {
val billerDetails =
BillerDetailsEntity(
billerId = myBillEntity.billerId,
billerName = myBillEntity.billerName,
billerLogoUrl = myBillEntity.billerLogoUrl,
status = myBillEntity.status,
isAdhoc = myBillEntity.isAdhoc,
fetchOption = EMPTY,
paymentAmountExactness = myBillEntity.paymentAmountExactness,
)
navigateToNextScreen(
billerDetails = billerDetails,
phoneNumberDetail = phoneNumberDetail,

View File

@@ -579,6 +579,7 @@ constructor(
phoneNumberDetail = phoneNumberDetail,
source = source,
initialSource = initialSource,
isBillSyncRequiredInBackground = false,
)
)
}

View File

@@ -41,6 +41,9 @@ constructor(
suspend fun fetchSavedBillsByCategory(category: String) =
myBillsDao.getBillsByCategory(categoryId = category)
fun fetchSavedBillsByCategoryAsFlow(category: String) =
myBillsDao.getBillsByCategoryAsFlow(categoryId = category)
suspend fun markBillAsPaid(
billId: String,
metricInfo: MetricInfo<RepoResult<BillMarkAsPaidResponse>>,

View File

@@ -72,6 +72,7 @@ import com.navi.bbps.feature.mybills.model.view.MyBillsState
import com.navi.bbps.feature.paybill.model.view.PayBillSource
import com.navi.bbps.feature.prepaidrecharge.model.view.OperatorItemEntity
import com.navi.bbps.feature.prepaidrecharge.model.view.PlanItemEntity
import com.navi.bbps.isPrepaidRechargePaidOrWithoutPlanDetails
import com.navi.bbps.isRedirectToCustomerInputRequired
import com.navi.common.di.CoroutineDispatcherProvider
import com.navi.common.network.models.isSuccessWithData
@@ -690,10 +691,7 @@ constructor(
myBillEntityToBillerDetailsEntityMapper.mapMyBillEntityToBillerDetailsEntity(
myBillEntity = myBillEntity
)
if (
myBillEntity.unpaidBillDetails?.planItemDetails.isNullOrEmpty() &&
myBillEntity.categoryId == CATEGORY_ID_MOBILE_PREPAID
) {
if (myBillEntity.isPrepaidRechargePaidOrWithoutPlanDetails()) {
_navigateToNextScreen.emit(
PrepaidRechargeScreenDestination(
phoneNumberDetail =

View File

@@ -27,6 +27,9 @@ interface MyBillsDao {
@Query("SELECT * FROM $NAVI_BBPS_TABLE_MY_SAVED_BILLS WHERE categoryId == :categoryId")
suspend fun getBillsByCategory(categoryId: String): List<MyBillEntity>
@Query("SELECT * FROM $NAVI_BBPS_TABLE_MY_SAVED_BILLS WHERE categoryId == :categoryId")
fun getBillsByCategoryAsFlow(categoryId: String): Flow<List<MyBillEntity>>
@Update suspend fun updateSavedBill(myBillEntity: MyBillEntity)
@Insert(onConflict = OnConflictStrategy.REPLACE)

View File

@@ -357,4 +357,9 @@
<string name="bbps_duplicate_payment_pending_secondary_cta_text">Pay again</string>
<string name="bbps_duplicate_payment_failed_mainline_text">Last payment on %s failed, know why?</string>
<string name="bbps_duplicate_payment_failed_annotated_text">know why?</string>
<string name="bbps_no_bills_due_description">You currently have no due bills for this biller.</string>
<string name="bbps_pay_bill_in_advance">Pay in advance</string>
<string name="bbps_recharge">Recharge</string>
<string name="bbps_bill_amount">bill amount</string>
<string name="bbps_view_all_plans">View all plans</string>
</resources>