Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ private fun ClientLoanAccountsScreen(
ClientLoanAccountsAction.ViewAccount(loan.id ?: 0),
)
is Actions.MakeRepayment -> onAction(
ClientLoanAccountsAction.MakeRepayment,
ClientLoanAccountsAction.MakeRepayment(loan.id ?: 0),
)

else -> null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class ClientLoanAccountsViewModel(
}

is ClientLoanAccountsAction.MakeRepayment -> {
sendEvent(ClientLoanAccountsEvent.MakeRepayment(state.clientId))
sendEvent(ClientLoanAccountsEvent.MakeRepayment(action.loanId))
}

ClientLoanAccountsAction.OnSearchClick -> {
Expand Down Expand Up @@ -157,7 +157,7 @@ sealed interface ClientLoanAccountsAction {
data object NavigateBack : ClientLoanAccountsAction
data object ToggleFilter : ClientLoanAccountsAction
data object Refresh : ClientLoanAccountsAction
data object MakeRepayment : ClientLoanAccountsAction
data class MakeRepayment(val loanId: Int) : ClientLoanAccountsAction
data class ViewAccount(val loanId: Int) : ClientLoanAccountsAction
data class UpdateSearchValue(val query: String) : ClientLoanAccountsAction
data object OnSearchClick : ClientLoanAccountsAction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ import com.mifos.feature.document.navigation.navigateToDocumentListScreen
import com.mifos.feature.groups.navigation.navigateToGroupDetailsScreen
import com.mifos.feature.loan.loanAccount.navigateToLoanAccountScreen
import com.mifos.feature.loan.loanAccountSummary.navigateToLoanAccountSummaryScreen
import com.mifos.feature.loan.loanRepayment.navigateToLoanRepaymentScreen
import com.mifos.feature.loan.navigation.loanDestination
import com.mifos.feature.loan.newLoanAccount.navigateToNewLoanAccountRoute
import com.mifos.feature.note.navigation.noteDestination
Expand Down Expand Up @@ -329,7 +330,7 @@ fun NavGraphBuilder.clientNavGraph(
clientLoanAccountsDestination(
navigateBack = navController::popBackStack,
navigateToViewAccount = navController::navigateToLoanAccountSummaryScreen,
navigateToMakeRepayment = {},
navigateToMakeRepayment = navController::navigateToLoanRepaymentScreen,
navController = navController,
)
clientIdentifiersListDestination(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,18 @@ internal fun LoanRepaymentScreen(
viewmodel: LoanRepaymentViewModel = koinViewModel(),
) {
val uiState by viewmodel.loanRepaymentUiState.collectAsStateWithLifecycle()
val arg by viewmodel.arg.collectAsStateWithLifecycle()

LaunchedEffect(key1 = Unit) {
viewmodel.checkDatabaseLoanRepaymentByLoanId()
}

LoanRepaymentScreen(
loanId = viewmodel.arg.loanId,
clientName = viewmodel.arg.clientName,
loanProductName = viewmodel.arg.loanProductName,
amountInArrears = viewmodel.arg.amountInArrears,
loanAccountNumber = viewmodel.arg.loanAccountNumber,
loanId = arg.loanId,
clientName = arg.clientName,
loanProductName = arg.loanProductName,
amountInArrears = arg.amountInArrears,
loanAccountNumber = arg.loanAccountNumber,
uiState = uiState,
navigateBack = navigateBack,
onRetry = { viewmodel.checkDatabaseLoanRepaymentByLoanId() },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import kotlinx.serialization.Serializable
data class LoanRepaymentScreenRoute(
var clientName: String = "",
var loanId: Int = 0,
var loanAccountNumber: String,
var loanProductName: String,
var loanAccountNumber: String = "",
var loanProductName: String = "",
var amountInArrears: Double? = 0.0,
)

Expand All @@ -45,3 +45,11 @@ fun NavController.navigateToLoanRepaymentScreen(loanWithAssociations: LoanWithAs
),
)
}

fun NavController.navigateToLoanRepaymentScreen(loanId: Int) {
navigate(
LoanRepaymentScreenRoute(
loanId = loanId,
),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,75 @@ package com.mifos.feature.loan.loanRepayment
import androidclient.feature.loan.generated.resources.Res
import androidclient.feature.loan.generated.resources.feature_loan_failed_to_load_loan_repayment
import androidclient.feature.loan.generated.resources.feature_loan_payment_failed
import androidclient.feature.loan.generated.resources.feature_loan_unknown_error_occured
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.toRoute
import com.mifos.core.common.utils.CurrencyFormatter
import com.mifos.core.common.utils.DataState
import com.mifos.core.data.repository.LoanAccountSummaryRepository
import com.mifos.core.data.repository.LoanRepaymentRepository
import com.mifos.room.entities.accounts.loans.LoanRepaymentRequestEntity
import com.mifos.room.entities.accounts.loans.LoanWithAssociationsEntity
import com.mifos.room.entities.templates.loans.LoanRepaymentTemplateEntity
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlin.math.round

class LoanRepaymentViewModel(
savedStateHandle: SavedStateHandle,
private val repository: LoanRepaymentRepository,
private val summaryRepository: LoanAccountSummaryRepository,
) : ViewModel() {

val arg = savedStateHandle.toRoute<LoanRepaymentScreenRoute>()
private val _arg = MutableStateFlow(savedStateHandle.toRoute<LoanRepaymentScreenRoute>())
val arg: StateFlow<LoanRepaymentScreenRoute> = _arg.asStateFlow()

private val _loanRepaymentUiState =
MutableStateFlow<LoanRepaymentUiState>(LoanRepaymentUiState.ShowProgressbar)
val loanRepaymentUiState: StateFlow<LoanRepaymentUiState> get() = _loanRepaymentUiState

init {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call checkDatabaseLoanRepaymentByLoanId() in this init block only. (and remove it from the loadLoanById function)

it would look like this

init {
    if (_arg.loanAccountNumber.isEmpty()) {
        loadLoanById()
    }
    checkDatabaseLoanRepaymentByLoanId()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I observed that checkDatabaseLoanRepaymentByLoanId() can be called regardless of the fact whether the loan details are being loaded or we already have from the args as in both we are getting the loanId by args but I have not called the function in viewmodel and kept it like it was before my changes because calling it only in init block may display stale data until the screen is in backstack . Suppose , if we navigate to another screen and navigate back to this screen then launched effect will load the data again but the init block will not do so until the viewmodel is destroyed and created again.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"If we navigate to another screen and navigate back to this screen then launched effect will load the data again"

Are you sure about this? because on the internet I see that LaunchedEffect runs only two times 1. Entering in the composition, 2. when the keys are updated. Navigating to other screen doesn't remove current screen from composition, current screen stays in the backstack. So navigating back will not add LaunchedEffect again in the composition and hence checkDatabaseLoanRepaymentByLoanId will not be called again I think.
Have you tested that?

Because if that's not happening then we should add a button to load them

Copy link
Contributor Author

@Kartikey15dem Kartikey15dem Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sahilshivekar
I cross checked by adding an empty notification screen and navigated to that screen and then navigated back and the launched effect code ran again . You can check once for account 053 in maria. Although I am still a bit confused as different LLMs are giving different answers and clarify it from any senior but keeping the launched effect is better in either case as it manually checked it .

Copy link
Contributor Author

@Kartikey15dem Kartikey15dem Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can check for loan account 053 in maria.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am also confused that why LaunchedEffect is again triggering if the composition is in the backstack.
Need to ask about it to a senior. Ask about it in daily standup tmrw if you are also unsure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sahilshivekar
But still I think it can be approved because using launchedeffect won't cause any harm, instead it will better.

if (_arg.value.loanAccountNumber.isEmpty()) {
loadLoanById()
}
}

fun loadLoanById() {
viewModelScope.launch {
summaryRepository.getLoanById(_arg.value.loanId).collect { dataState ->
when (dataState) {
is DataState.Loading -> {
_loanRepaymentUiState.value = LoanRepaymentUiState.ShowProgressbar
}

is DataState.Success -> {
val loanWithAssociations = dataState.data ?: LoanWithAssociationsEntity()
_arg.value = _arg.value.copy(
loanId = loanWithAssociations.id,
clientName = loanWithAssociations.clientName,
loanProductName = loanWithAssociations.loanProductName,
amountInArrears = loanWithAssociations.summary.totalOverdue,
loanAccountNumber = loanWithAssociations.accountNo,
)
}

is DataState.Error -> {
_loanRepaymentUiState.value = LoanRepaymentUiState.ShowError(
Res.string.feature_loan_unknown_error_occured,
)
}
}
}
}
}

fun loanLoanRepaymentTemplate() {
viewModelScope.launch {
repository.getLoanRepayTemplate(arg.loanId).collect { state ->
repository.getLoanRepayTemplate(_arg.value.loanId).collect { state ->
when (state) {
is DataState.Error ->
_loanRepaymentUiState.value =
Expand All @@ -64,7 +105,7 @@ class LoanRepaymentViewModel(
_loanRepaymentUiState.value = LoanRepaymentUiState.ShowProgressbar

try {
val loanRepaymentResponse = repository.submitPayment(arg.loanId, request)
val loanRepaymentResponse = repository.submitPayment(_arg.value.loanId, request)
_loanRepaymentUiState.value =
LoanRepaymentUiState.ShowPaymentSubmittedSuccessfully(
loanRepaymentResponse,
Expand All @@ -78,7 +119,7 @@ class LoanRepaymentViewModel(

fun checkDatabaseLoanRepaymentByLoanId() {
viewModelScope.launch {
repository.getDatabaseLoanRepaymentByLoanId(arg.loanId).collect { state ->
repository.getDatabaseLoanRepaymentByLoanId(_arg.value.loanId).collect { state ->
when (state) {
is DataState.Error ->
_loanRepaymentUiState.value =
Expand Down