diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/db/NaviPayAppDatabase.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/db/NaviPayAppDatabase.kt index 68c2b6b39e..3b8e56d1a4 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/db/NaviPayAppDatabase.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/db/NaviPayAppDatabase.kt @@ -636,5 +636,19 @@ val NAVI_PAY_APP_DATABASE_MIGRATION_21_22 = db.execSQL( "ALTER TABLE `$NAVI_PAY_DATABASE_VPA_TABLE_NAME` ADD COLUMN `vpaReason` TEXT NOT NULL DEFAULT 'UNKNOWN'" ) + + /* + * Migration to fix orderTimestamp format inconsistency causing sorting issues. + * + * Problem: Order history had mixed timestamp formats: + * - Network sync: UTC format (2025-06-18T12:10:19.673Z) + * - Local transactions: IST format (2025-06-18T17:34:34.452+05:30) + * + * Solution: Force re-sync from server to get all data in consistent UTC format. + * This ensures proper chronological sorting in order history. + */ + db.execSQL( + "DELETE FROM $NAVI_PAY_DATABASE_SYNC_TABLE_NAME WHERE syncKey = '$NAVI_PAY_SYNC_TABLE_ORDER_HISTORY_KEY'" + ) } } diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/management/common/paymentsummary/viewmodel/PaymentSummaryViewModel.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/management/common/paymentsummary/viewmodel/PaymentSummaryViewModel.kt index 62050d41c7..4ea52f61ce 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/management/common/paymentsummary/viewmodel/PaymentSummaryViewModel.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/management/common/paymentsummary/viewmodel/PaymentSummaryViewModel.kt @@ -779,6 +779,10 @@ constructor( orderEntity?.let { currentOrder -> val updateOrderEntity = currentOrder.copy( + orderTimestamp = + getDateTimeObjectFromEpochString( + epochMillis = currentOrder.orderTimestamp.millis + ), orderDetails = currentOrder.orderDetails.let { currentOrderDetails -> currentOrderDetails.copy( @@ -792,12 +796,6 @@ constructor( } ) }, - orderTimestamp = - getDateTimeObjectFromEpochString( - epochMillis = currentOrder.orderTimestamp.millis - ), - // updating timestamp here to maintain consistent format in - // local db if the db entry is not updated by api response ) orderDetailsRepository.updateOrder(orderEntity = updateOrderEntity) } diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/management/common/transaction/util/OrderEntityMapperUtil.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/management/common/transaction/util/OrderEntityMapperUtil.kt index 452ed0d4fc..a61eba480c 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/management/common/transaction/util/OrderEntityMapperUtil.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/management/common/transaction/util/OrderEntityMapperUtil.kt @@ -29,6 +29,7 @@ import com.navi.pay.tstore.list.model.view.OrderPaymentMode import com.navi.pay.tstore.list.model.view.OrderStatusOfView import com.navi.pay.utils.NAVI_PAY_TRANSACTION_HISTORY_TAG_SEPARATOR import com.navi.rr.utils.ext.toJson +import org.joda.time.DateTimeZone internal fun TransactionEntity.toOrderEntity(tstoreOrderId: String): OrderEntity = OrderEntity( @@ -49,7 +50,7 @@ internal fun TransactionEntity.toOrderEntity(tstoreOrderId: String): OrderEntity orderTitle = getOrderTitleToDisplay(), paymentMode = OrderPaymentMode.UPI, orderDescription = getOrderDescriptionToDisplay(), - orderTimestamp = transactionTimestamp, + orderTimestamp = transactionTimestamp.withZone(DateTimeZone.UTC), orderImageUrl = otherUserInfo.split(NAVI_PAY_TRANSACTION_HISTORY_TAG_SEPARATOR).getOrElse(3) { "" diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/tstore/details/repository/OrderDetailsRepository.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/tstore/details/repository/OrderDetailsRepository.kt index 008c671e7a..a623ec0ad1 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/tstore/details/repository/OrderDetailsRepository.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/tstore/details/repository/OrderDetailsRepository.kt @@ -23,6 +23,7 @@ import com.navi.payments.shared.core.network.service.PaymentsSharedRetrofitServi import com.navi.payments.shared.feature.arc.model.network.ArcExactOfferRequest import com.navi.payments.shared.feature.arc.model.network.ArcExactOfferResponse import javax.inject.Inject +import kotlinx.coroutines.flow.Flow class OrderDetailsRepository @Inject @@ -50,6 +51,10 @@ constructor( return orderDao.getOrderEntity(orderId) } + fun getOrderEntityAsFlow(orderId: String): Flow { + return orderDao.getOrderEntityAsFlow(orderId) + } + suspend fun updateNotificationsPermission( notificationSettings: List ): RepoResult { diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/tstore/details/viewmodel/OrderDetailsViewModel.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/tstore/details/viewmodel/OrderDetailsViewModel.kt index 04fb6b4f71..cb14179f8b 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/tstore/details/viewmodel/OrderDetailsViewModel.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/tstore/details/viewmodel/OrderDetailsViewModel.kt @@ -191,10 +191,14 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -252,8 +256,16 @@ constructor( savedStateHandle.get("orderReferenceId")?.takeIf { it.isNotEmpty() } ?: naviPayActivityDataProvider.getString("orderReferenceId").orEmpty() - private val _orderEntity = MutableStateFlow(null) - val orderEntity = _orderEntity.asStateFlow() + val orderEntity = + orderDetailsRepository + .getOrderEntityAsFlow(orderId = orderReferenceId) + .flowOn(dispatcherProvider.io) + .distinctUntilChanged() + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(), + initialValue = null, + ) private val _linkedAccounts = MutableStateFlow>(emptyList()) val linkedAccounts = _linkedAccounts.asStateFlow() @@ -588,13 +600,25 @@ constructor( private suspend fun getOrderDetails(): IsError { - val orderDetails = orderDetailsRepository.getOrderEntity(orderId = orderReferenceId) + val orderDetails = orderEntity.first { it != null } if (orderDetails == null) { _clearAndNavigateBack.emit(true) return true } - updateOrderEntity(orderEntity = orderDetails) + if (orderDetails.paymentStatus.isFailed() || orderDetails.orderStatusOfView.isFailed()) { + val orderErrorEntity = + orderErrorMappingManager.getErrorEntityByCode( + errorCode = orderDetails.orderDetails.errorCode + ) + naviPayAnalytics.onOrderErrorDetailsFetchedDevEvent( + errorCode = orderDetails.orderDetails.errorCode.orEmpty(), + orderErrorEntity = orderErrorEntity, + isExperimentEnabled = true, + productType = orderDetails.productType, + ) + _orderErrorEntity.update { orderErrorEntity } + } _orderDetailsMetadataProvider.update { OrderDetailsMetadataProvider(orderEntity = orderEntity.value) @@ -1578,11 +1602,15 @@ constructor( val arcExactOfferResponseData = arcExactOfferResponse.data!! val updatedOrderEntity = - _orderEntity.value?.copy( + orderEntity.value?.copy( + orderTimestamp = + getDateTimeObjectFromEpochString( + orderEntity.value?.orderTimestamp?.millis ?: 0L + ), orderDetails = orderEntity.value ?.orderDetails!! - .copy(isArcProtected = arcExactOfferResponseData.isArcProtected.orFalse()) + .copy(isArcProtected = arcExactOfferResponseData.isArcProtected.orFalse()), ) updateOrderEntity(orderEntity = updatedOrderEntity) @@ -1673,7 +1701,11 @@ constructor( (orderEntity.value?.paymentStatus != newPaymentStatus) val updatedOrderEntity = - _orderEntity.value?.copy( + orderEntity.value?.copy( + orderTimestamp = + getDateTimeObjectFromEpochString( + epochMillis = orderEntity.value?.orderTimestamp?.millis ?: 0L + ), orderDetails = orderEntity.value ?.orderDetails!! @@ -1682,11 +1714,8 @@ constructor( paymentStatus = newPaymentStatus, ) - updateOrderEntity(orderEntity = updatedOrderEntity) - if (isStatusChanged) { - orderDetailsRepository.updateOrder(orderEntity = orderEntity.value!!) - getOrderDetails() + updateOrderEntity(orderEntity = updatedOrderEntity!!) prepareArcStatusWidgetProperties() if ( newOrderStatusOfView != OrderStatusOfView.Pending || @@ -1700,6 +1729,7 @@ constructor( launch { liteAccountSyncUseCase.execute(screenName = screenName) } } } + preparePaymentStatusWidgetProperties() } prepareRefundStatusWidgetProperties() @@ -1948,9 +1978,13 @@ constructor( updateScratchCardStatus(ScratchCardState.None) - _orderEntity.value?.let { currentOrder -> + orderEntity.value?.let { currentOrder -> val updatedOrder = currentOrder.copy( + orderTimestamp = + getDateTimeObjectFromEpochString( + currentOrder.orderTimestamp.millis + ), orderDetails = currentOrder.orderDetails.let { currentOrderDetails -> currentOrderDetails.copy( @@ -1966,17 +2000,9 @@ constructor( } ) }, - orderTimestamp = - getDateTimeObjectFromEpochString( - epochMillis = currentOrder.orderTimestamp.millis - ), - // updating timestamp here to maintain consistent format in - // local db if the db entry is not updated by api response ) - updateOrderEntity(updatedOrder) + orderDetailsRepository.updateOrder(orderEntity = updatedOrder) } - - _orderEntity.value?.let { orderDetailsRepository.updateOrder(it) } } is ScratchCardBackResponse.Success -> { @@ -2005,9 +2031,13 @@ constructor( } ) - _orderEntity.value?.let { currentOrder -> + orderEntity.value?.let { currentOrder -> val updatedOrder = currentOrder.copy( + orderTimestamp = + getDateTimeObjectFromEpochString( + currentOrder.orderTimestamp.millis + ), orderDetails = currentOrder.orderDetails.let { currentOrderDetails -> currentOrderDetails.copy( @@ -2023,17 +2053,9 @@ constructor( } ) }, - orderTimestamp = - getDateTimeObjectFromEpochString( - epochMillis = currentOrder.orderTimestamp.millis - ), - // updating timestamp here to maintain consistent format in - // local db if the db entry is not updated by api response ) - updateOrderEntity(updatedOrder) + orderDetailsRepository.updateOrder(orderEntity = updatedOrder) } - - _orderEntity.value?.let { orderDetailsRepository.updateOrder(it) } } else -> {} @@ -2045,7 +2067,7 @@ constructor( private suspend fun updateOrderEntity(orderEntity: OrderEntity?) { if (orderEntity == null) return - _orderEntity.update { orderEntity } + orderDetailsRepository.updateOrder(orderEntity = orderEntity) if (orderEntity.paymentStatus.isFailed() || orderEntity.orderStatusOfView.isFailed()) { val orderErrorEntity = diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/tstore/list/db/dao/OrderDao.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/tstore/list/db/dao/OrderDao.kt index b180cc6d16..0fb4f45669 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/tstore/list/db/dao/OrderDao.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/tstore/list/db/dao/OrderDao.kt @@ -32,6 +32,11 @@ interface OrderDao { ) suspend fun getOrderEntity(orderId: String): OrderEntity? + @Query( + "SELECT * FROM $NAVI_PAY_DATABASE_T_STORE_ORDER_HISTORY_TABLE_NAME WHERE orderReferenceId = :orderId" + ) + fun getOrderEntityAsFlow(orderId: String): Flow + @Update suspend fun updateOrderEntity(orderEntity: OrderEntity) @Query(