diff --git a/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/DataTableListRepository.kt b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/DataTableListRepository.kt index da30396222..6bc0a11fa2 100644 --- a/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/DataTableListRepository.kt +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/DataTableListRepository.kt @@ -14,6 +14,7 @@ import com.mifos.core.model.objects.payloads.GroupLoanPayload import com.mifos.core.network.model.LoansPayload import com.mifos.room.entities.accounts.loans.Loan import com.mifos.room.entities.client.ClientPayloadEntity +import io.ktor.client.statement.HttpResponse import kotlinx.coroutines.flow.Flow /** @@ -21,7 +22,7 @@ import kotlinx.coroutines.flow.Flow */ interface DataTableListRepository { - fun createLoansAccount(loansPayload: LoansPayload?): Flow> + fun createLoansAccount(loansPayload: LoansPayload?): Flow> fun createGroupLoansAccount(loansPayload: GroupLoanPayload?): Flow> diff --git a/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanAccountRepository.kt b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanAccountRepository.kt index 0fce5e24e6..a8d535eaf2 100644 --- a/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanAccountRepository.kt +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanAccountRepository.kt @@ -12,8 +12,8 @@ package com.mifos.core.data.repository import com.mifos.core.common.utils.DataState import com.mifos.core.model.objects.organisations.LoanProducts import com.mifos.core.network.model.LoansPayload -import com.mifos.room.entities.accounts.loans.Loan import com.mifos.room.entities.templates.loans.LoanTemplate +import io.ktor.client.statement.HttpResponse import kotlinx.coroutines.flow.Flow /** @@ -25,5 +25,5 @@ interface LoanAccountRepository { fun getLoansAccountTemplate(clientId: Int, productId: Int): Flow> - fun createLoansAccount(loansPayload: LoansPayload): Flow> + fun createLoansAccount(loansPayload: LoansPayload): Flow> } diff --git a/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/DataTableListRepositoryImp.kt b/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/DataTableListRepositoryImp.kt index 0f354bb5b7..7ab8efebac 100644 --- a/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/DataTableListRepositoryImp.kt +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/DataTableListRepositoryImp.kt @@ -19,6 +19,7 @@ import com.mifos.core.network.datamanager.DataManagerLoan import com.mifos.core.network.model.LoansPayload import com.mifos.room.entities.accounts.loans.Loan import com.mifos.room.entities.client.ClientPayloadEntity +import io.ktor.client.statement.HttpResponse import kotlinx.coroutines.flow.Flow /** @@ -30,7 +31,7 @@ class DataTableListRepositoryImp( private val dataManagerClient: DataManagerClient, ) : DataTableListRepository { - override fun createLoansAccount(loansPayload: LoansPayload?): Flow> { + override fun createLoansAccount(loansPayload: LoansPayload?): Flow> { return dataManagerLoan.createLoansAccount(loansPayload) .asDataStateFlow() } diff --git a/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/LoanAccountRepositoryImp.kt b/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/LoanAccountRepositoryImp.kt index c0f5db2605..ff2c4f2876 100644 --- a/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/LoanAccountRepositoryImp.kt +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/LoanAccountRepositoryImp.kt @@ -15,8 +15,8 @@ import com.mifos.core.data.repository.LoanAccountRepository import com.mifos.core.model.objects.organisations.LoanProducts import com.mifos.core.network.datamanager.DataManagerLoan import com.mifos.core.network.model.LoansPayload -import com.mifos.room.entities.accounts.loans.Loan import com.mifos.room.entities.templates.loans.LoanTemplate +import io.ktor.client.statement.HttpResponse import kotlinx.coroutines.flow.Flow /** @@ -38,7 +38,7 @@ class LoanAccountRepositoryImp( .asDataStateFlow() } - override fun createLoansAccount(loansPayload: LoansPayload): Flow> { + override fun createLoansAccount(loansPayload: LoansPayload): Flow> { return dataManagerLoan.createLoansAccount(loansPayload) .asDataStateFlow() } diff --git a/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosSweetError.kt b/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosSweetError.kt index 7b47ab8a42..42068e73d6 100644 --- a/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosSweetError.kt +++ b/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosSweetError.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.mifos.core.designsystem.theme.MifosTheme import core.designsystem.generated.resources.Res @@ -39,6 +40,7 @@ import org.jetbrains.compose.ui.tooling.preview.Preview @Composable fun MifosSweetError( message: String, + isShowLoadMsg: Boolean = true, modifier: Modifier = Modifier .fillMaxSize() .padding(18.dp) @@ -52,14 +54,16 @@ fun MifosSweetError( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { - Text( - text = stringResource(Res.string.core_designsystem_unable_to_load), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.secondary, - ) + if (isShowLoadMsg) { + Text( + text = stringResource(Res.string.core_designsystem_unable_to_load), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.secondary, + ) + } Text( text = message, - style = MaterialTheme.typography.bodyMedium, + style = MaterialTheme.typography.bodyMedium.copy(textAlign = TextAlign.Center), color = MaterialTheme.colorScheme.secondary, ) if (isRetryEnabled) { diff --git a/core/domain/src/commonMain/kotlin/com/mifos/core/domain/useCases/CreateLoanAccountUseCase.kt b/core/domain/src/commonMain/kotlin/com/mifos/core/domain/useCases/CreateLoanAccountUseCase.kt index 4248a44b5a..fe631c1f97 100644 --- a/core/domain/src/commonMain/kotlin/com/mifos/core/domain/useCases/CreateLoanAccountUseCase.kt +++ b/core/domain/src/commonMain/kotlin/com/mifos/core/domain/useCases/CreateLoanAccountUseCase.kt @@ -12,13 +12,13 @@ package com.mifos.core.domain.useCases import com.mifos.core.common.utils.DataState import com.mifos.core.data.repository.LoanAccountRepository import com.mifos.core.network.model.LoansPayload -import com.mifos.room.entities.accounts.loans.Loan +import io.ktor.client.statement.HttpResponse import kotlinx.coroutines.flow.Flow class CreateLoanAccountUseCase( private val loanAccountRepository: LoanAccountRepository, ) { - operator fun invoke(loansPayload: LoansPayload): Flow> = + operator fun invoke(loansPayload: LoansPayload): Flow> = loanAccountRepository.createLoansAccount(loansPayload) } diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerLoan.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerLoan.kt index 0076238d63..cd613b9aa2 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerLoan.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerLoan.kt @@ -15,7 +15,6 @@ import com.mifos.core.network.BaseApiManager import com.mifos.core.network.GenericResponse import com.mifos.core.network.model.LoansPayload import com.mifos.room.entities.PaymentTypeOptionEntity -import com.mifos.room.entities.accounts.loans.Loan import com.mifos.room.entities.accounts.loans.LoanRepaymentRequestEntity import com.mifos.room.entities.accounts.loans.LoanRepaymentResponseEntity import com.mifos.room.entities.accounts.loans.LoanWithAssociationsEntity @@ -23,6 +22,7 @@ import com.mifos.room.entities.templates.loans.LoanRepaymentTemplateEntity import com.mifos.room.entities.templates.loans.LoanTemplate import com.mifos.room.entities.templates.loans.LoanTransactionTemplate import com.mifos.room.helper.LoanDaoHelper +import io.ktor.client.statement.HttpResponse import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first @@ -90,7 +90,7 @@ class DataManagerLoan( return mBaseApiManager.loanService.getLoansAccountTemplate(clientId, productId) } - fun createLoansAccount(loansPayload: LoansPayload?): Flow { + fun createLoansAccount(loansPayload: LoansPayload?): Flow { return mBaseApiManager.loanService.createLoansAccount(loansPayload) } diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/model/LoansPayload.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/model/LoansPayload.kt index 4edf512460..2d119ad02f 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/model/LoansPayload.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/model/LoansPayload.kt @@ -44,4 +44,5 @@ data class LoansPayload( var fundId: Int? = null, var linkAccountId: Int? = null, var dataTables: ArrayList? = null, + var externalId: String? = null, ) diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/services/LoanService.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/services/LoanService.kt index bf9639d41d..e204da369c 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/services/LoanService.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/services/LoanService.kt @@ -31,6 +31,7 @@ import de.jensklingenberg.ktorfit.http.GET import de.jensklingenberg.ktorfit.http.POST import de.jensklingenberg.ktorfit.http.Path import de.jensklingenberg.ktorfit.http.Query +import io.ktor.client.statement.HttpResponse import kotlinx.coroutines.flow.Flow /** @@ -75,7 +76,7 @@ interface LoanService { fun getAllLoans(): Flow> @POST(APIEndPoint.CREATE_LOANS_ACCOUNTS) - fun createLoansAccount(@Body loansPayload: LoansPayload?): Flow + fun createLoansAccount(@Body loansPayload: LoansPayload?): Flow @GET(APIEndPoint.CREATE_LOANS_ACCOUNTS + "/template?templateType=individual") fun getLoansAccountTemplate( diff --git a/core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosStepper.kt b/core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosStepper.kt index f976819f50..07fc2f2928 100644 --- a/core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosStepper.kt +++ b/core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosStepper.kt @@ -11,10 +11,9 @@ package com.mifos.core.ui.components import androidx.compose.foundation.background import androidx.compose.foundation.clickable -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.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -66,22 +65,23 @@ fun MifosStepper( ), horizontalAlignment = Alignment.CenterHorizontally, ) { - LazyRow( - state = listState, + Box( modifier = Modifier .clip(shape = DesignToken.shapes.medium) .background(MaterialTheme.colorScheme.primary) - .padding(vertical = DesignToken.padding.largeIncreasedExtra) - .padding(start = DesignToken.padding.small) + .padding( + vertical = DesignToken.padding.largeIncreasedExtra, + ) .fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, + contentAlignment = Alignment.Center, ) { - steps.forEachIndexed { index, step -> - repeat(2) { it -> - item { - Row( - verticalAlignment = Alignment.Top, - ) { + LazyRow( + state = listState, + contentPadding = PaddingValues(horizontal = DesignToken.padding.small), + ) { + steps.forEachIndexed { index, step -> + repeat(2) { it -> + item { if (it == 0) { Column( horizontalAlignment = Alignment.CenterHorizontally, @@ -107,7 +107,6 @@ fun MifosStepper( color = MaterialTheme.colorScheme.primary, ) } - Spacer(modifier = Modifier.height(DesignToken.padding.small)) BasicText( text = step.name, @@ -120,16 +119,14 @@ fun MifosStepper( ), ) } - } else { - if (index != steps.lastIndex) { - Box( - modifier = Modifier - .padding(vertical = DesignToken.padding.large) - .width(DesignToken.padding.small) - .height(1.dp) - .background(AppColors.stepperColor), - ) - } + } else if (index != steps.lastIndex) { + Box( + modifier = Modifier + .padding(vertical = DesignToken.padding.large) + .width(DesignToken.padding.small) + .height(1.dp) + .background(AppColors.stepperColor), + ) } } } @@ -154,7 +151,7 @@ private fun MifosStepperDemo() { MifosStepper( steps = steps, - currentIndex = 2, + currentIndex = 0, onStepChange = { }, modifier = Modifier .fillMaxWidth(), diff --git a/core/ui/src/commonMain/kotlin/com/mifos/core/ui/util/TextFieldsValidator.kt b/core/ui/src/commonMain/kotlin/com/mifos/core/ui/util/TextFieldsValidator.kt index ad8a72cee0..bee8680bb0 100644 --- a/core/ui/src/commonMain/kotlin/com/mifos/core/ui/util/TextFieldsValidator.kt +++ b/core/ui/src/commonMain/kotlin/com/mifos/core/ui/util/TextFieldsValidator.kt @@ -46,6 +46,7 @@ object TextFieldsValidator { fun doubleNumberValidator(input: String): StringResource? { return when { + input.isBlank() -> Res.string.error_field_empty input.count { it == '.' } > 1 -> Res.string.error_invalid_number input.any { !it.isDigit() && it != '.' } -> Res.string.error_digits_only input.toDoubleOrNull() == null -> Res.string.error_invalid_number diff --git a/feature/client/src/commonMain/composeResources/values/strings.xml b/feature/client/src/commonMain/composeResources/values/strings.xml index 9c0d1ec018..921055a48c 100644 --- a/feature/client/src/commonMain/composeResources/values/strings.xml +++ b/feature/client/src/commonMain/composeResources/values/strings.xml @@ -480,6 +480,7 @@ Apply for new share account Apply for new recurring deposit account Apply for new fixed deposit account + This client account is currently inactive. Activation is required before a new account can be created. Details Terms diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationRoute.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationRoute.kt index 38d69d23b8..b6b61e1d9e 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationRoute.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationRoute.kt @@ -16,7 +16,8 @@ import kotlinx.serialization.Serializable @Serializable data class ClientApplyNewApplicationRoute( - val clientId: Int = -1, + val clientId: Int, + val status: String, ) fun NavGraphBuilder.clientApplyNewApplicationRoute( @@ -41,6 +42,6 @@ fun NavGraphBuilder.clientApplyNewApplicationRoute( } } -fun NavController.navigateToClientApplyNewApplicationScreen(clientId: Int) { - this.navigate(ClientApplyNewApplicationRoute(clientId)) +fun NavController.navigateToClientApplyNewApplicationScreen(clientId: Int, status: String) { + this.navigate(ClientApplyNewApplicationRoute(clientId, status)) } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsScreen.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsScreen.kt index 1af152e0c0..849c50a8d4 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsScreen.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsScreen.kt @@ -16,6 +16,7 @@ import androidclient.feature.client.generated.resources.client_apply_new_applica import androidclient.feature.client.generated.resources.client_apply_new_applications_apply_loan_account import androidclient.feature.client.generated.resources.client_apply_new_applications_apply_recurring_account import androidclient.feature.client.generated.resources.client_apply_new_applications_apply_savings_account +import androidclient.feature.client.generated.resources.client_apply_new_applications_client_inactive_msg import androidclient.feature.client.generated.resources.client_apply_new_applications_fixed_account import androidclient.feature.client.generated.resources.client_apply_new_applications_loan_account import androidclient.feature.client.generated.resources.client_apply_new_applications_recurring_account @@ -40,12 +41,14 @@ import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import com.mifos.core.designsystem.component.MifosScaffold +import com.mifos.core.designsystem.component.MifosSweetError import com.mifos.core.designsystem.theme.DesignToken import com.mifos.core.designsystem.theme.MifosTypography import com.mifos.core.ui.components.MifosBreadcrumbNavBar import com.mifos.core.ui.components.MifosRowCard import com.mifos.core.ui.util.EventsEffect import com.mifos.core.ui.util.TextUtil +import com.mifos.room.entities.client.ClientStatusEntity import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import org.jetbrains.compose.resources.DrawableResource @@ -72,9 +75,15 @@ internal fun ClientApplyNewApplicationsScreen( is ClientApplyNewApplicationsEvent.OnActionClick -> { when (event.action) { ClientApplyNewApplicationsItem.NewFixedAccount -> onNavigateApplyFixedAccount() - ClientApplyNewApplicationsItem.NewLoanAccount -> onNavigateApplyLoanAccount(state.clientId) + ClientApplyNewApplicationsItem.NewLoanAccount -> onNavigateApplyLoanAccount( + state.clientId, + ) + ClientApplyNewApplicationsItem.NewRecurringAccount -> onNavigateApplyRecurringAccount() - ClientApplyNewApplicationsItem.NewSavingsAccount -> onNavigateApplySavingsAccount(state.clientId) + ClientApplyNewApplicationsItem.NewSavingsAccount -> onNavigateApplySavingsAccount( + state.clientId, + ) + ClientApplyNewApplicationsItem.NewShareAccount -> onNavigateApplyShareAccount() } } @@ -82,6 +91,7 @@ internal fun ClientApplyNewApplicationsScreen( } ClientApplyNewApplicationsContent( + state = state, navController = navController, onAction = remember(viewModel) { { viewModel.trySendAction(it) } }, ) @@ -89,6 +99,7 @@ internal fun ClientApplyNewApplicationsScreen( @Composable private fun ClientApplyNewApplicationsContent( + state: ClientApplyNewApplicationsState, navController: NavController, onAction: (ClientApplyNewApplicationsAction) -> Unit, ) { @@ -102,35 +113,41 @@ private fun ClientApplyNewApplicationsContent( modifier = Modifier.fillMaxSize().padding(paddingValues), ) { MifosBreadcrumbNavBar(navController) - Column( - modifier = Modifier.padding(horizontal = DesignToken.padding.large), - ) { - Text( - text = stringResource(Res.string.client_apply_new_applications_title), - style = MifosTypography.labelLargeEmphasized, - ) - - Spacer(modifier = Modifier.height(DesignToken.spacing.medium)) - - clientApplyNewApplicationsItems.forEach { - MifosRowCard( - title = stringResource(it.title), - imageVector = it.icon, - leftValues = listOf( - TextUtil( - text = stringResource(it.subTitle), - style = MifosTypography.bodySmall, - color = MaterialTheme.colorScheme.secondary, - ), - ), - rightValues = emptyList(), - modifier = Modifier - .clickable { - onAction(ClientApplyNewApplicationsAction.OnActionClick(it)) - } - .padding(vertical = DesignToken.padding.medium), + if (state.status == ClientStatusEntity.STATUS_ACTIVE) { + Column( + modifier = Modifier.padding(horizontal = DesignToken.padding.large), + ) { + Text( + text = stringResource(Res.string.client_apply_new_applications_title), + style = MifosTypography.labelLargeEmphasized, ) + Spacer(modifier = Modifier.height(DesignToken.spacing.medium)) + clientApplyNewApplicationsItems.forEach { + MifosRowCard( + title = stringResource(it.title), + imageVector = it.icon, + leftValues = listOf( + TextUtil( + text = stringResource(it.subTitle), + style = MifosTypography.bodySmall, + color = MaterialTheme.colorScheme.secondary, + ), + ), + rightValues = emptyList(), + modifier = Modifier + .clickable { + onAction(ClientApplyNewApplicationsAction.OnActionClick(it)) + } + .padding(vertical = DesignToken.padding.medium), + ) + } } + } else { + MifosSweetError( + message = stringResource(Res.string.client_apply_new_applications_client_inactive_msg), + isShowLoadMsg = false, + isRetryEnabled = false, + ) } } } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsViewModel.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsViewModel.kt index 802ffc53b3..88d88f4d09 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsViewModel.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsViewModel.kt @@ -17,27 +17,39 @@ class ClientApplyNewApplicationsViewModel( val savedStateHandle: SavedStateHandle, ) : BaseViewModel( initialState = run { - ClientApplyNewApplicationsState(savedStateHandle.toRoute().clientId) + val route = savedStateHandle.toRoute() + ClientApplyNewApplicationsState( + clientId = route.clientId, + status = route.status, + ) }, ) { override fun handleAction(action: ClientApplyNewApplicationsAction) { when (action) { - ClientApplyNewApplicationsAction.NavigateBack -> sendEvent(ClientApplyNewApplicationsEvent.NavigateBack) - is ClientApplyNewApplicationsAction.OnActionClick -> sendEvent(ClientApplyNewApplicationsEvent.OnActionClick(action.action)) + ClientApplyNewApplicationsAction.NavigateBack -> sendEvent( + ClientApplyNewApplicationsEvent.NavigateBack, + ) + + is ClientApplyNewApplicationsAction.OnActionClick -> sendEvent( + ClientApplyNewApplicationsEvent.OnActionClick(action.action), + ) } } } data class ClientApplyNewApplicationsState( val clientId: Int, + val status: String, ) sealed interface ClientApplyNewApplicationsEvent { data object NavigateBack : ClientApplyNewApplicationsEvent - data class OnActionClick(val action: ClientApplyNewApplicationsItem) : ClientApplyNewApplicationsEvent + data class OnActionClick(val action: ClientApplyNewApplicationsItem) : + ClientApplyNewApplicationsEvent } sealed interface ClientApplyNewApplicationsAction { data object NavigateBack : ClientApplyNewApplicationsAction - data class OnActionClick(val action: ClientApplyNewApplicationsItem) : ClientApplyNewApplicationsAction + data class OnActionClick(val action: ClientApplyNewApplicationsItem) : + ClientApplyNewApplicationsAction } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientDetailsProfile/ClientProfileDetailsNavigation.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientDetailsProfile/ClientProfileDetailsNavigation.kt index 9db1f29011..655a7c5c92 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientDetailsProfile/ClientProfileDetailsNavigation.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientDetailsProfile/ClientProfileDetailsNavigation.kt @@ -30,7 +30,7 @@ fun NavGraphBuilder.clientProfileDetailsDestination( navigateToUpdateDefaultAccount: (Int) -> Unit, navigateToCollateral: (Int) -> Unit, navigateToUpdateSignature: (Int, String, String) -> Unit, - navigateToApplyNewApplication: (Int) -> Unit, + navigateToApplyNewApplication: (Int, String) -> Unit, ) { composable { ClientProfileDetailsScreen( diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientDetailsProfile/ClientProfileDetailsScreen.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientDetailsProfile/ClientProfileDetailsScreen.kt index 685c6cc50a..d2d99aa843 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientDetailsProfile/ClientProfileDetailsScreen.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientDetailsProfile/ClientProfileDetailsScreen.kt @@ -80,7 +80,7 @@ internal fun ClientProfileDetailsScreen( navigateToUpdateDetails: (Int) -> Unit, navigateToClientTransfer: (Int) -> Unit, navigateToClientClosure: (Int) -> Unit, - navigateToApplyNewApplication: (Int) -> Unit, + navigateToApplyNewApplication: (Int, String) -> Unit, navigateToUpdateDefaultAccount: (Int) -> Unit, navigateToCollateral: (Int) -> Unit, navigateToUpdateSignature: (Int, String, String) -> Unit, @@ -99,7 +99,10 @@ internal fun ClientProfileDetailsScreen( when (event.action) { ClientProfileDetailsActionItem.AddCharge -> {} ClientProfileDetailsActionItem.ApplyNewApplication -> { - navigateToApplyNewApplication(state.client?.id ?: -1) + navigateToApplyNewApplication( + state.client?.id ?: -1, + state.client?.status?.value ?: "", + ) } ClientProfileDetailsActionItem.AssignStaff -> { navigateToAssignStaff(state.client?.id ?: -1) diff --git a/feature/loan/src/commonMain/composeResources/values/strings.xml b/feature/loan/src/commonMain/composeResources/values/strings.xml index 2a92b227de..ecb30e8208 100644 --- a/feature/loan/src/commonMain/composeResources/values/strings.xml +++ b/feature/loan/src/commonMain/composeResources/values/strings.xml @@ -23,7 +23,7 @@ External ID Principal - Number of Repayments + Number of Repayments* Nominal Repaid Every Loan Terms @@ -32,7 +32,7 @@ Per month Term Amortization - Interest Calculation Period + Interest Calculation Period* Repayment Strategy Interest Type Method Calculate Interest for exact days in @@ -143,8 +143,8 @@ Loan Officer Loan Purpose Fund - Submission Date - Expected Disbursement + Submission Date* + Expected Disbursement* Savings Linkage Link Savings Create Standing Instructions at disbursement @@ -178,22 +178,23 @@ Interest Calculations Moratorium? Collateral Data - Principal + Principal* Loan Term - Term Frequency - Number of Repayments + Term Frequency* + Number of Repayments* First Repayment Date Interest Charged From - Repaid Every - Frequency + Repaid Every* + Frequency* + Frequency Select On Select Day - Nominal interest rate (in %) + Nominal interest rate (in %)* Interest Method - Amortization + Amortization* Is Equal Amortization Loan Schedule Type - Repayment Strategy + Repayment Strategy* Balloon Repayment Amount Interest Calculation Period Calculate interest for exact days in partial period @@ -273,5 +274,8 @@ View Active Charges + + This field cannot be empty + \ No newline at end of file diff --git a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/NewLoanAccountScreen.kt b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/NewLoanAccountScreen.kt index 2dac344bfb..5e71a89e40 100644 --- a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/NewLoanAccountScreen.kt +++ b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/NewLoanAccountScreen.kt @@ -17,8 +17,8 @@ import androidclient.feature.loan.generated.resources.add_new_collateral import androidclient.feature.loan.generated.resources.back import androidclient.feature.loan.generated.resources.collateral import androidclient.feature.loan.generated.resources.edit_charge +import androidclient.feature.loan.generated.resources.feature_loan_account_created_successfully import androidclient.feature.loan.generated.resources.feature_loan_cancel -import androidclient.feature.loan.generated.resources.new_loan_account_title import androidclient.feature.loan.generated.resources.quantity import androidclient.feature.loan.generated.resources.step_charges import androidclient.feature.loan.generated.resources.step_details @@ -37,8 +37,10 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier @@ -50,6 +52,7 @@ import com.mifos.core.designsystem.component.MifosBasicDialog import com.mifos.core.designsystem.component.MifosBottomSheet import com.mifos.core.designsystem.component.MifosOutlinedTextField import com.mifos.core.designsystem.component.MifosScaffold +import com.mifos.core.designsystem.component.MifosSweetError import com.mifos.core.designsystem.component.MifosTextFieldConfig import com.mifos.core.designsystem.component.MifosTextFieldDropdown import com.mifos.core.designsystem.theme.DesignToken @@ -58,10 +61,10 @@ import com.mifos.core.ui.components.Actions import com.mifos.core.ui.components.AddChargeBottomSheet import com.mifos.core.ui.components.MifosActionsChargeListingComponent import com.mifos.core.ui.components.MifosBreadcrumbNavBar -import com.mifos.core.ui.components.MifosErrorComponent import com.mifos.core.ui.components.MifosListingComponentOutline import com.mifos.core.ui.components.MifosListingRowItem import com.mifos.core.ui.components.MifosProgressIndicator +import com.mifos.core.ui.components.MifosProgressIndicatorOverlay import com.mifos.core.ui.components.MifosStepper import com.mifos.core.ui.components.MifosTwoButtonRow import com.mifos.core.ui.components.Step @@ -71,8 +74,11 @@ import com.mifos.feature.loan.newLoanAccount.pages.DetailsPage import com.mifos.feature.loan.newLoanAccount.pages.PreviewPage import com.mifos.feature.loan.newLoanAccount.pages.SchedulePage import com.mifos.feature.loan.newLoanAccount.pages.TermsPage +import kotlinx.coroutines.delay +import org.jetbrains.compose.resources.getString import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel +import kotlin.time.ExperimentalTime @Composable internal fun NewLoanAccountScreen( @@ -104,6 +110,8 @@ internal fun NewLoanAccountScreen( ) } +@OptIn(ExperimentalTime::class) +@Suppress("SuspiciousIndentation") @Composable private fun NewLoanAccountScaffold( navController: NavController, @@ -111,6 +119,8 @@ private fun NewLoanAccountScaffold( modifier: Modifier = Modifier, onAction: (NewLoanAccountAction) -> Unit, ) { + val snackbarHostState = remember { SnackbarHostState() } + val steps = listOf( Step(stringResource(Res.string.step_details)) { DetailsPage( @@ -145,27 +155,55 @@ private fun NewLoanAccountScaffold( ) MifosScaffold( - title = stringResource(Res.string.new_loan_account_title), - onBackPressed = { onAction(NewLoanAccountAction.NavigateBack) }, modifier = modifier, + snackbarHostState = snackbarHostState, ) { paddingValues -> - if (state.dialogState !is NewLoanAccountState.DialogState.Error) { - Column( - Modifier.fillMaxSize().padding(paddingValues), - ) { - MifosBreadcrumbNavBar( - navController, - ) - MifosStepper( - steps = steps, - currentIndex = state.currentStep, - onStepChange = { newIndex -> - onAction(NewLoanAccountAction.OnStepChange(newIndex)) - }, - modifier = Modifier - .fillMaxWidth(), + Column( + modifier.fillMaxSize(), + ) { + MifosBreadcrumbNavBar( + navController, + ) + when (state.screenState) { + is NewLoanAccountState.ScreenState.Loading -> MifosProgressIndicator() + is NewLoanAccountState.ScreenState.Success -> { + MifosStepper( + steps = steps, + currentIndex = state.currentStep, + onStepChange = { newIndex -> + onAction(NewLoanAccountAction.OnStepChange(newIndex)) + }, + modifier = Modifier + .fillMaxWidth(), + ) + } + + is NewLoanAccountState.ScreenState.Error -> { + MifosSweetError( + message = state.screenState.message, + onclick = { onAction(NewLoanAccountAction.Retry) }, + ) + } + + null -> Unit + } + } + + if (state.isOverLayLoadingActive) { + MifosProgressIndicatorOverlay() + } + + if (state.responseErrorMsg != null) { + LaunchedEffect(state.launchEffectKey) { + snackbarHostState.showSnackbar( + message = state.responseErrorMsg, ) + + if (state.responseErrorMsg == getString(Res.string.feature_loan_account_created_successfully)) { + delay(1000) + onAction(NewLoanAccountAction.Finish) + } } } } @@ -177,18 +215,6 @@ private fun NewLoanAccountDialogs( onAction: (NewLoanAccountAction) -> Unit, ) { when (state.dialogState) { - is NewLoanAccountState.DialogState.Error -> { - MifosErrorComponent( - message = state.dialogState.message, - isRetryEnabled = true, - onRetry = { - onAction(NewLoanAccountAction.Retry) - }, - ) - } - - null -> Unit - NewLoanAccountState.DialogState.AddNewCollateral -> AddNewCollateralDialog( state = state, onAction = onAction, @@ -217,9 +243,7 @@ private fun NewLoanAccountDialogs( isOverDue = true, ) - NewLoanAccountState.DialogState.Loading -> { - MifosProgressIndicator() - } + null -> Unit } } @@ -258,7 +282,11 @@ private fun AddNewCollateralDialog( MifosOutlinedTextField( value = state.collateralQuantity.toString(), onValueChange = { - onAction(NewLoanAccountAction.OnCollateralQuantityChanged(it.toIntOrNull() ?: 0)) + onAction( + NewLoanAccountAction.OnCollateralQuantityChanged( + it.toIntOrNull() ?: 0, + ), + ) }, label = stringResource(Res.string.quantity), config = MifosTextFieldConfig( @@ -397,7 +425,13 @@ private fun AddNewChargeDialog( onAction(NewLoanAccountAction.OnChargesDatePick(show)) }, onDateChange = { newDate -> - onAction(NewLoanAccountAction.OnChargesDateChange(DateHelper.getDateAsStringFromLong(newDate))) + onAction( + NewLoanAccountAction.OnChargesDateChange( + DateHelper.getDateAsStringFromLong( + newDate, + ), + ), + ) }, onAmountChange = { amount -> onAction(NewLoanAccountAction.OnChargesAmountChange(amount)) @@ -447,11 +481,17 @@ private fun ShowChargesDialog( onActionClicked = { action -> when (action) { is Actions.Delete -> { - onAction(NewLoanAccountAction.DeleteChargeFromSelectedCharges(index)) + onAction( + NewLoanAccountAction.DeleteChargeFromSelectedCharges( + index, + ), + ) } + is Actions.Edit -> { onAction(NewLoanAccountAction.EditChargeDialog(index)) } + else -> {} } }, diff --git a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/NewLoanAccountViewModel.kt b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/NewLoanAccountViewModel.kt index ae6b5f5492..eeea5a9b89 100644 --- a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/NewLoanAccountViewModel.kt +++ b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/NewLoanAccountViewModel.kt @@ -12,19 +12,21 @@ package com.mifos.feature.loan.newLoanAccount import androidclient.feature.loan.generated.resources.Res import androidclient.feature.loan.generated.resources.account_number import androidclient.feature.loan.generated.resources.disbursement_date +import androidclient.feature.loan.generated.resources.feature_loan_account_created_successfully import androidclient.feature.loan.generated.resources.installment_paid import androidclient.feature.loan.generated.resources.principle_paid_off import androidclient.feature.loan.generated.resources.total_installments import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import androidx.navigation.toRoute -import co.touchlab.kermit.Logger import com.mifos.core.common.utils.CurrencyFormatter import com.mifos.core.common.utils.DataState import com.mifos.core.common.utils.DateHelper import com.mifos.core.data.repository.ClientDetailsRepository import com.mifos.core.data.repository.SyncClientsDialogRepository +import com.mifos.core.data.util.Error import com.mifos.core.data.util.NetworkMonitor +import com.mifos.core.data.util.extractErrorMessage import com.mifos.core.domain.useCases.CreateLoanAccountUseCase import com.mifos.core.domain.useCases.GetAllLoanUseCase import com.mifos.core.domain.useCases.GetLoansAccountTemplateUseCase @@ -32,13 +34,14 @@ import com.mifos.core.model.objects.organisations.LoanProducts import com.mifos.core.network.model.CollateralItem import com.mifos.core.network.model.LoansPayload import com.mifos.core.ui.util.BaseViewModel -import com.mifos.core.ui.util.TextFieldsValidator import com.mifos.feature.loan.newLoanAccount.NewLoanAccountState.DialogState import com.mifos.room.entities.accounts.loans.LoanWithAssociationsEntity import com.mifos.room.entities.templates.loans.LoanTemplate import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.getString +import kotlin.random.Random import kotlin.time.Clock import kotlin.time.ExperimentalTime @@ -103,16 +106,6 @@ internal class NewLoanAccountViewModel( action, ) - is NewLoanAccountAction.OnDetailsSubmit -> handleOnDetailsSubmit() - - is NewLoanAccountAction.Internal.OnReceivingLoanAccounts -> handleAllLoansResponse( - action.loans, - ) - - is NewLoanAccountAction.Internal.OnReceivingLoanTemplate -> handleLoanTemplateResponse( - action.template, - ) - is NewLoanAccountAction.OnFirstRepaymentDateChange -> handleFirstRepaymentDateChange( action, ) @@ -246,8 +239,6 @@ internal class NewLoanAccountViewModel( } } - NewLoanAccountAction.GotoPreviousStep -> {} - NewLoanAccountAction.SubmitLoanApplication -> submitLoanApplication() } } @@ -255,30 +246,32 @@ internal class NewLoanAccountViewModel( private fun submitLoanApplication() { viewModelScope.launch { val payload = LoansPayload( - loanOfficerId = state.loanOfficerIndex, - principal = state.principalAmount, + loanOfficerId = if (state.loanOfficerIndex == -1) null else state.loanTemplate?.loanOfficerOptions[state.loanOfficerIndex]?.id, + principal = state.principalAmount.toDouble(), clientId = state.clientId, allowPartialPeriodInterestCalcualtion = state.isCheckedInterestPartialPeriod, - amortizationType = state.nominalAmortizationIndex, + amortizationType = state.loanTemplate?.amortizationTypeOptions[state.nominalAmortizationIndex]?.id, dateFormat = DateHelper.SHORT_MONTH, - interestCalculationPeriodType = state.interestCalculationPeriodIndex, - interestRatePerPeriod = state.nominalInterestRate, - interestType = state.nominalInterestMethodIndex, - loanTermFrequency = state.termFrequencyIndex, - loanTermFrequencyType = state.termFrequencyIndex, + interestCalculationPeriodType = state.loanTemplate?.interestCalculationPeriodTypeOptions[state.interestCalculationPeriodIndex]?.id, + interestRatePerPeriod = state.nominalInterestRate.toDouble(), + interestType = state.loanTemplate?.interestTypeOptions[state.nominalInterestMethodIndex]?.id, + loanTermFrequency = state.noOfRepayments * state.repaidEvery, + loanTermFrequencyType = state.loanTemplate?.termFrequencyTypeOptions[state.termFrequencyIndex]?.id, loanType = "individual", locale = "en", numberOfRepayments = state.noOfRepayments, - productId = state.loanProductSelected, + productId = state.productId, repaymentEvery = state.repaidEvery, - repaymentFrequencyDayOfWeekType = state.selectedDayIndex, - repaymentFrequencyNthDayType = state.selectedOnIndex, - repaymentFrequencyType = state.repaymentStrategyIndex, + repaymentFrequencyDayOfWeekType = if (state.selectedDayIndex == -1) null else state.loanTemplate?.repaymentFrequencyDaysOfWeekTypeOptions[state.selectedDayIndex]?.id, + repaymentFrequencyNthDayType = if (state.selectedOnIndex == -1) null else state.loanTemplate?.repaymentFrequencyNthDayTypeOptions[state.selectedOnIndex]?.id, + repaymentFrequencyType = state.loanTemplate?.termFrequencyTypeOptions[state.termFrequencyIndex]?.id, expectedDisbursementDate = state.expectedDisbursementDate, submittedOnDate = state.submissionDate, - loanPurposeId = state.loanPurposeIndex, - fundId = state.fundIndex, - linkAccountId = state.linkSavingsIndex, + loanPurposeId = if (state.loanPurposeIndex == -1) null else state.loanTemplate?.loanPurposeOptions[state.loanPurposeIndex]?.id, + fundId = if (state.fundIndex == -1) null else state.loanTemplate?.fundOptions[state.fundIndex]?.id, + linkAccountId = if (state.linkSavingsIndex == -1) null else state.loanTemplate?.accountLinkingOptions[state.linkSavingsIndex]?.id, + transactionProcessingStrategyCode = state.loanTemplate?.transactionProcessingStrategyOptions[state.repaymentStrategyIndex]?.code, + externalId = state.externalId, ) loanUseCase(payload).collect { dataState -> @@ -286,8 +279,8 @@ internal class NewLoanAccountViewModel( is DataState.Error -> { mutableStateFlow.update { it.copy( - dialogState = NewLoanAccountState.DialogState.Error(dataState.message), - isLoading = false, + screenState = NewLoanAccountState.ScreenState.Error(dataState.message), + isOverLayLoadingActive = false, ) } } @@ -295,14 +288,32 @@ internal class NewLoanAccountViewModel( DataState.Loading -> { mutableStateFlow.update { it.copy( - isLoading = true, + isOverLayLoadingActive = true, ) } } is DataState.Success -> { - Logger.d("DebugTtt success") - sendEvent(NewLoanAccountEvent.Finish) + val error = extractErrorMessage(dataState.data) + + // Successful create lona account if response error not found + if (error == Error.MSG_NOT_FOUND) { + mutableStateFlow.update { + it.copy( + responseErrorMsg = getString(Res.string.feature_loan_account_created_successfully), + launchEffectKey = Random.nextInt(), + isOverLayLoadingActive = false, + ) + } + } else { + mutableStateFlow.update { + it.copy( + isOverLayLoadingActive = false, + responseErrorMsg = error, + launchEffectKey = Random.nextInt(), + ) + } + } } } } @@ -419,18 +430,25 @@ internal class NewLoanAccountViewModel( } private fun handleSelectedDayIndexChange(action: NewLoanAccountAction.OnSelectedDayIndexChange) { - mutableStateFlow.update { it.copy(selectedDayIndex = action.index) } + mutableStateFlow.update { + it.copy( + selectedDayIndex = action.index, + ) + } } private fun handleSelectedOnIndexChange(action: NewLoanAccountAction.OnSelectedOnIndexChange) { - mutableStateFlow.update { it.copy(selectedOnIndex = action.index) } + mutableStateFlow.update { + it.copy( + selectedOnIndex = action.index, + ) + } } private fun handleNominalInterestRateChange(action: NewLoanAccountAction.OnNominalInterestRateChange) { mutableStateFlow.update { it.copy( nominalInterestRate = action.rate, - nominalInterestRateText = action.text, ) } } @@ -440,11 +458,19 @@ internal class NewLoanAccountViewModel( } private fun handleNominalMethodIndexChange(action: NewLoanAccountAction.OnNominalMethodIndexChange) { - mutableStateFlow.update { it.copy(nominalInterestMethodIndex = action.index) } + mutableStateFlow.update { + it.copy( + nominalInterestMethodIndex = action.index, + ) + } } private fun handleNominalAmortizationIndexChange(action: NewLoanAccountAction.OnNominalAmortizationIndexChange) { - mutableStateFlow.update { it.copy(nominalAmortizationIndex = action.index) } + mutableStateFlow.update { + it.copy( + nominalAmortizationIndex = action.index, + ) + } } private fun handleEqualAmortizationCheckChange(action: NewLoanAccountAction.OnEqualAmortizationCheckChange) { @@ -452,7 +478,11 @@ internal class NewLoanAccountViewModel( } private fun handleRepaymentStrategyIndexChange(action: NewLoanAccountAction.OnRepaymentStrategyIndexChange) { - mutableStateFlow.update { it.copy(repaymentStrategyIndex = action.index) } + mutableStateFlow.update { + it.copy( + repaymentStrategyIndex = action.index, + ) + } } private fun handleBalloonRepaymentAmountChange(action: NewLoanAccountAction.OnBalloonRepaymentAmountChange) { @@ -460,7 +490,11 @@ internal class NewLoanAccountViewModel( } private fun handleInterestCalculationPeriodIndexChange(action: NewLoanAccountAction.OnInterestCalculationPeriodIndexChange) { - mutableStateFlow.update { it.copy(interestCalculationPeriodIndex = action.index) } + mutableStateFlow.update { + it.copy( + interestCalculationPeriodIndex = action.index, + ) + } } private fun handleInterestPartialPeriodCheckChange(action: NewLoanAccountAction.OnInterestPartialPeriodCheckChange) { @@ -612,14 +646,15 @@ internal class NewLoanAccountViewModel( mutableStateFlow.update { it.copy( principalAmount = action.amount, - principalAmountText = action.text, ) } } private fun handleTermFrequencyIndexChange(action: NewLoanAccountAction.OnTermFrequencyIndexChange) { mutableStateFlow.update { - it.copy(termFrequencyIndex = action.index) + it.copy( + termFrequencyIndex = action.index, + ) } } @@ -637,30 +672,24 @@ internal class NewLoanAccountViewModel( sendEvent(NewLoanAccountEvent.NavigateBack) } - private fun handleOnDetailsSubmit() { - mutableStateFlow.update { - it.copy(externalIdError = null) - } - val externalIdError = TextFieldsValidator.stringValidator(state.externalId) - if (externalIdError == null) { - moveToNextStep() - } else { - mutableStateFlow.update { - it.copy(externalIdError = externalIdError) - } - } - } - private fun handleFinish() { sendEvent(NewLoanAccountEvent.Finish) } private fun handleStepChange(action: NewLoanAccountAction.OnStepChange) { - mutableStateFlow.update { it.copy(currentStep = action.newIndex) } + mutableStateFlow.update { + it.copy( + currentStep = action.newIndex, + ) + } } private fun handleProductNameChange(action: NewLoanAccountAction.OnProductNameChange) { - mutableStateFlow.update { it.copy(loanProductSelected = action.index) } + mutableStateFlow.update { + it.copy( + loanProductSelected = action.index, + ) + } loadLoanAccountTemplate(state.productLoans[action.index].id ?: -1) } @@ -669,15 +698,27 @@ internal class NewLoanAccountViewModel( } private fun handleFundChange(action: NewLoanAccountAction.OnFundChange) { - mutableStateFlow.update { it.copy(fundIndex = action.index) } + mutableStateFlow.update { + it.copy( + fundIndex = action.index, + ) + } } private fun handleLoanOfficerChange(action: NewLoanAccountAction.OnLoanOfficerChange) { - mutableStateFlow.update { it.copy(loanOfficerIndex = action.index) } + mutableStateFlow.update { + it.copy( + loanOfficerIndex = action.index, + ) + } } private fun handleLoanPurposeChange(action: NewLoanAccountAction.OnLoanPurposeChange) { - mutableStateFlow.update { it.copy(loanPurposeIndex = action.index) } + mutableStateFlow.update { + it.copy( + loanPurposeIndex = action.index, + ) + } } private fun handleExpectedDisbursementDateChange(action: NewLoanAccountAction.OnExpectedDisbursementDateChange) { @@ -697,7 +738,11 @@ internal class NewLoanAccountViewModel( } private fun handleLinkSavingsChange(action: NewLoanAccountAction.OnLinkSavingsChange) { - mutableStateFlow.update { it.copy(linkSavingsIndex = action.index) } + mutableStateFlow.update { + it.copy( + linkSavingsIndex = action.index, + ) + } } private fun handleStandingInstructionsChange(action: NewLoanAccountAction.OnStandingInstructionsChange) { @@ -738,7 +783,7 @@ internal class NewLoanAccountViewModel( } else { mutableStateFlow.update { it.copy( - dialogState = NewLoanAccountState.DialogState.Error("No internet connection"), + screenState = NewLoanAccountState.ScreenState.Error("Network Error"), ) } } @@ -764,66 +809,63 @@ internal class NewLoanAccountViewModel( private fun loadAllLoans() = viewModelScope.launch { getAllLoanUseCase().collect { result -> - sendAction(NewLoanAccountAction.Internal.OnReceivingLoanAccounts(result)) - } - } - - fun loadLoanAccountTemplate(productId: Int) = viewModelScope.launch { - getLoansAccountTemplateUseCase(state.clientId, productId).collect { result -> - sendAction(NewLoanAccountAction.Internal.OnReceivingLoanTemplate(result)) - } - } - - private fun handleAllLoansResponse(result: DataState>) { - when (result) { - is DataState.Error -> mutableStateFlow.update { - it.copy(dialogState = NewLoanAccountState.DialogState.Error(result.message)) - } + when (result) { + is DataState.Error -> mutableStateFlow.update { + it.copy( + screenState = NewLoanAccountState.ScreenState.Error(result.message), + ) + } - is DataState.Loading -> mutableStateFlow.update { - it.copy(dialogState = NewLoanAccountState.DialogState.Loading) - } + is DataState.Loading -> mutableStateFlow.update { + it.copy(screenState = NewLoanAccountState.ScreenState.Loading) + } - is DataState.Success -> mutableStateFlow.update { - it.copy( - dialogState = null, - productLoans = result.data, - ) + is DataState.Success -> mutableStateFlow.update { + it.copy( + screenState = NewLoanAccountState.ScreenState.Success, + productLoans = result.data, + ) + } } } } - private fun handleLoanTemplateResponse(result: DataState) { - when (result) { - is DataState.Error -> mutableStateFlow.update { - it.copy( - dialogState = NewLoanAccountState.DialogState.Error(result.message), - isOverLayLoadingActive = false, - ) - } + fun loadLoanAccountTemplate(productId: Int) = viewModelScope.launch { + mutableStateFlow.update { + it.copy(productId = productId) + } + getLoansAccountTemplateUseCase(state.clientId, productId).collect { result -> + when (result) { + is DataState.Error -> mutableStateFlow.update { + it.copy( + screenState = NewLoanAccountState.ScreenState.Error(result.message), + isOverLayLoadingActive = false, + ) + } - is DataState.Loading -> mutableStateFlow.update { - it.copy(isOverLayLoadingActive = true) - } + is DataState.Loading -> mutableStateFlow.update { + it.copy( + isOverLayLoadingActive = true, + ) + } - is DataState.Success -> mutableStateFlow.update { - it.copy( - dialogState = null, - isOverLayLoadingActive = false, - loanTemplate = result.data, - principalAmount = result.data.principal ?: 0.0, - principalAmountText = result.data.principal.toString(), - noOfRepayments = result.data.numberOfRepayments ?: 0, - repaidEvery = result.data.repaymentEvery ?: 0, - nominalInterestRate = result.data.interestRatePerPeriod ?: 0.0, - nominalInterestRateText = result.data.interestRatePerPeriod.toString(), - termFrequencyIndex = result.data.termFrequencyTypeOptions.indexOfFirst { item -> item.value == result.data.termPeriodFrequencyType?.value }, - nominalFrequencyIndex = result.data.interestRateFrequencyTypeOptions.indexOfFirst { item -> item.value == result.data.interestRateFrequencyType?.value }, - nominalInterestMethodIndex = result.data.interestTypeOptions.indexOfFirst { item -> item.value == result.data.interestType?.value }, - nominalAmortizationIndex = result.data.amortizationTypeOptions.indexOfFirst { item -> item.value == result.data.amortizationType?.value }, - repaymentStrategyIndex = result.data.transactionProcessingStrategyOptions.indexOfFirst { item -> item.code == result.data.transactionProcessingStrategyCode }, - interestCalculationPeriodIndex = result.data.interestCalculationPeriodTypeOptions.indexOfFirst { item -> item.code == result.data.interestCalculationPeriodType?.code }, - ) + is DataState.Success -> mutableStateFlow.update { + it.copy( + screenState = NewLoanAccountState.ScreenState.Success, + isOverLayLoadingActive = false, + loanTemplate = result.data, + principalAmount = (result.data.principal ?: 0).toString(), + noOfRepayments = result.data.numberOfRepayments ?: 0, + repaidEvery = result.data.repaymentEvery ?: 0, + nominalInterestRate = (result.data.interestRatePerPeriod ?: 0).toString(), + nominalAmortizationIndex = result.data.amortizationTypeOptions.indexOfFirst { item -> item.value == result.data.amortizationType?.value }, + termFrequencyIndex = result.data.termFrequencyTypeOptions.indexOfFirst { item -> item.value == result.data.termPeriodFrequencyType?.value }, + nominalFrequencyIndex = result.data.interestRateFrequencyTypeOptions.indexOfFirst { item -> item.value == result.data.interestRateFrequencyType?.value }, + nominalInterestMethodIndex = result.data.interestTypeOptions.indexOfFirst { item -> item.value == result.data.interestType?.value }, + repaymentStrategyIndex = result.data.transactionProcessingStrategyOptions.indexOfFirst { item -> item.code == result.data.transactionProcessingStrategyCode }, + interestCalculationPeriodIndex = result.data.interestCalculationPeriodTypeOptions.indexOfFirst { item -> item.value == result.data.interestCalculationPeriodType?.value }, + ) + } } } } @@ -835,8 +877,8 @@ internal class NewLoanAccountViewModel( is DataState.Error -> { mutableStateFlow.update { it.copy( - dialogState = DialogState.Error(dataState.message), - isLoading = false, + screenState = NewLoanAccountState.ScreenState.Error(dataState.message), + isOverLayLoadingActive = false, ) } } @@ -844,7 +886,7 @@ internal class NewLoanAccountViewModel( DataState.Loading -> { mutableStateFlow.update { it.copy( - isLoading = true, + isOverLayLoadingActive = true, ) } } @@ -880,10 +922,10 @@ internal class NewLoanAccountViewModel( mutableStateFlow.update { it.copy( - dialogState = null, repaymentSchedules = schedulerDetails, + screenState = NewLoanAccountState.ScreenState.Success, loanWithAssociationsEntity = dataState.data, - isLoading = false, + isOverLayLoadingActive = false, ) } } @@ -895,23 +937,22 @@ internal class NewLoanAccountViewModel( data class NewLoanAccountState @OptIn(ExperimentalTime::class) constructor( - val isLoading: Boolean = false, + val responseErrorMsg: String? = null, + val launchEffectKey: Int? = null, val networkConnection: Boolean = false, val clientId: Int, + val productId: Int? = null, val productLoans: List = emptyList(), - val loanProductSelected: Int = -1, val loanWithAssociationsEntity: LoanWithAssociationsEntity = LoanWithAssociationsEntity(), val repaymentSchedules: Map = emptyMap(), val loanTemplate: LoanTemplate? = null, val currentStep: Int = 0, val totalSteps: Int = 4, val dialogState: DialogState? = null, + val screenState: ScreenState? = null, val isOverLayLoadingActive: Boolean = false, val externalId: String = "", val externalIdError: StringResource? = null, - val loanOfficerIndex: Int = -1, - val loanPurposeIndex: Int = -1, - val fundIndex: Int = -1, val submissionDate: String = DateHelper.getDateAsStringFromLong( Clock.System.now().toEpochMilliseconds(), ), @@ -920,13 +961,9 @@ constructor( Clock.System.now().toEpochMilliseconds(), ), val showExpectedDisbursementDatePick: Boolean = false, - val linkSavingsIndex: Int = -1, val isCheckedStandingInstructions: Boolean = false, - - val principalAmount: Double = 0.0, - val principalAmountText: String = "", + val principalAmount: String = "0", val noOfRepayments: Int = 0, - val termFrequencyIndex: Int = -1, val firstRepaymentDate: String = DateHelper.getDateAsStringFromLong( Clock.System.now().toEpochMilliseconds(), ), @@ -935,53 +972,64 @@ constructor( ), val showFirstRepaymentDatePick: Boolean = false, val showInterestChargedFromDatePick: Boolean = false, - val repaidEvery: Int = 1, - val selectedOnIndex: Int = -1, - val selectedDayIndex: Int = -1, - val nominalInterestRate: Double = 0.0, - val nominalInterestRateText: String = "", - val nominalFrequencyIndex: Int = -1, - val nominalInterestMethodIndex: Int = -1, - val nominalAmortizationIndex: Int = -1, + val repaidEvery: Int = 0, + val nominalInterestRate: String = "0", val isCheckedEqualAmortization: Boolean = false, - val repaymentStrategyIndex: Int = -1, - val balloonRepaymentAmount: Int = 0, - val interestCalculationPeriodIndex: Int = -1, val isCheckedInterestPartialPeriod: Boolean = false, val arrearsTolerance: Int = 0, val interestFreePeriod: Int = 0, val moratoriumGraceOnPrincipalPayment: Int = 0, val moratoriumGraceOnInterestPayment: Int = 0, val moratoriumOnArrearsAgeing: Int = 0, - val collaterals: List = emptyList(), val addedCollaterals: List = emptyList(), - val collateralSelectedIndex: Int = -1, val collateralQuantity: Int = 0, val collateralTotal: Double = 0.0, val totalCollateral: Double = 0.0, - - val chooseChargeIndex: Int = -1, val addedCharges: List = emptyList(), val chargeDate: String = DateHelper.getDateAsStringFromLong( Clock.System.now().toEpochMilliseconds(), ), + val balloonRepaymentAmount: Int = 0, val showChargesDatePick: Boolean = false, val chargeAmount: String = "", + /** these are use in dropDown field for change the value, + * it is not actual value for the field + */ + val loanProductSelected: Int = -1, + val chooseChargeIndex: Int = -1, + val collateralSelectedIndex: Int = -1, + val repaymentStrategyIndex: Int = -1, + val interestCalculationPeriodIndex: Int = -1, + val nominalFrequencyIndex: Int = -1, + val nominalInterestMethodIndex: Int = -1, + val nominalAmortizationIndex: Int = -1, + val selectedOnIndex: Int = -1, + val selectedDayIndex: Int = -1, + val termFrequencyIndex: Int = -1, + val linkSavingsIndex: Int = -1, + val loanOfficerIndex: Int = -1, + val loanPurposeIndex: Int = -1, + val fundIndex: Int = -1, + ) { sealed interface DialogState { - data class Error(val message: String) : DialogState data object AddNewCollateral : DialogState data class AddNewCharge(val edit: Boolean, val index: Int = -1) : DialogState data object ShowCollaterals : DialogState data object ShowCharges : DialogState data object ShowOverDueCharges : DialogState - data object Loading : DialogState + } + + sealed interface ScreenState { + data object Loading : ScreenState + data object Success : ScreenState + data class Error(val message: String) : ScreenState } val isDetailsNextEnabled = - loanProductSelected != -1 && externalId.isNotEmpty() && submissionDate.isNotEmpty() && expectedDisbursementDate.isNotEmpty() + loanProductSelected != -1 && submissionDate.isNotEmpty() && expectedDisbursementDate.isNotEmpty() val isCollateralBtnEnabled = collateralQuantity != 0 && collateralSelectedIndex != -1 } @@ -1008,14 +1056,7 @@ sealed interface NewLoanAccountAction { data class OnExpectedDisbursementDatePick(val state: Boolean) : NewLoanAccountAction data class OnLinkSavingsChange(val index: Int) : NewLoanAccountAction data class OnStandingInstructionsChange(val state: Boolean) : NewLoanAccountAction - data object OnDetailsSubmit : NewLoanAccountAction - - sealed interface Internal : NewLoanAccountAction { - data class OnReceivingLoanAccounts(val loans: DataState>) : Internal - data class OnReceivingLoanTemplate(val template: DataState) : Internal - } - - data class OnPrincipalAmountChange(val amount: Double, val text: String) : NewLoanAccountAction + data class OnPrincipalAmountChange(val amount: String) : NewLoanAccountAction data class OnNoOfRepaymentsChange(val number: Int) : NewLoanAccountAction data class OnTermFrequencyIndexChange(val index: Int) : NewLoanAccountAction data class OnFirstRepaymentDateChange(val date: String) : NewLoanAccountAction @@ -1025,7 +1066,9 @@ sealed interface NewLoanAccountAction { data class OnRepaidEveryChange(val number: Int) : NewLoanAccountAction data class OnSelectedOnIndexChange(val index: Int) : NewLoanAccountAction data class OnSelectedDayIndexChange(val index: Int) : NewLoanAccountAction - data class OnNominalInterestRateChange(val rate: Double, val text: String) : NewLoanAccountAction + data class OnNominalInterestRateChange(val rate: String) : + NewLoanAccountAction + data class OnNominalFrequencyIndexChange(val index: Int) : NewLoanAccountAction data class OnNominalMethodIndexChange(val index: Int) : NewLoanAccountAction data class OnNominalAmortizationIndexChange(val index: Int) : NewLoanAccountAction @@ -1060,7 +1103,6 @@ sealed interface NewLoanAccountAction { data class EditCharge(val index: Int) : NewLoanAccountAction data object RepaymentScheduler : NewLoanAccountAction data object SubmitLoanApplication : NewLoanAccountAction - data object GotoPreviousStep : NewLoanAccountAction } data class CreatedCollateral( diff --git a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/DetailsPage.kt b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/DetailsPage.kt index 85bc62f65e..1cf82d72bd 100644 --- a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/DetailsPage.kt +++ b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/DetailsPage.kt @@ -48,7 +48,6 @@ import androidx.compose.ui.Modifier import com.mifos.core.common.utils.DateHelper import com.mifos.core.designsystem.component.MifosDatePickerTextField import com.mifos.core.designsystem.component.MifosOutlinedTextField -import com.mifos.core.designsystem.component.MifosTextFieldConfig import com.mifos.core.designsystem.component.MifosTextFieldDropdown import com.mifos.core.designsystem.theme.DesignToken import com.mifos.core.designsystem.theme.MifosTypography @@ -78,7 +77,6 @@ fun DetailsPage( } }, ) - if (state.showSubmissionDatePick) { DatePickerDialog( onDismissRequest = { @@ -89,7 +87,13 @@ fun DetailsPage( onClick = { onAction(NewLoanAccountAction.OnSubmissionDatePick(false)) submissionDatePickerState.selectedDateMillis?.let { - onAction(NewLoanAccountAction.OnSubmissionDateChange(DateHelper.getDateAsStringFromLong(it))) + onAction( + NewLoanAccountAction.OnSubmissionDateChange( + DateHelper.getDateAsStringFromLong( + it, + ), + ), + ) } }, ) { Text(stringResource(Res.string.feature_loan_select)) } @@ -116,7 +120,11 @@ fun DetailsPage( onClick = { onAction(NewLoanAccountAction.OnExpectedDisbursementDatePick(false)) expectedDisbursementDatePickerState.selectedDateMillis?.let { - onAction(NewLoanAccountAction.OnExpectedDisbursementDateChange(DateHelper.getDateAsStringFromLong(it))) + onAction( + NewLoanAccountAction.OnExpectedDisbursementDateChange( + DateHelper.getDateAsStringFromLong(it), + ), + ) } }, ) { Text(stringResource(Res.string.feature_loan_select)) } @@ -158,7 +166,6 @@ fun DetailsPage( }, label = stringResource(Res.string.product_name), ) - if (state.loanTemplate != null) { MifosOutlinedTextField( value = state.externalId, @@ -166,10 +173,6 @@ fun DetailsPage( onAction(NewLoanAccountAction.OnExternalIdChange(it)) }, label = stringResource(Res.string.external_id), - config = MifosTextFieldConfig( - isError = state.externalIdError != null, - errorText = if (state.externalIdError != null)stringResource(state.externalIdError) else null, - ), ) Spacer(Modifier.height(DesignToken.padding.large)) MifosTextFieldDropdown( @@ -271,6 +274,7 @@ fun DetailsPage( Spacer(Modifier.height(DesignToken.padding.large)) } } + MifosTwoButtonRow( firstBtnText = stringResource(Res.string.back), secondBtnText = stringResource(Res.string.next), @@ -278,7 +282,7 @@ fun DetailsPage( onAction(NewLoanAccountAction.NavigateBack) }, onSecondBtnClick = { - onAction(NewLoanAccountAction.OnDetailsSubmit) + onAction(NewLoanAccountAction.NextStep) }, isSecondButtonEnabled = state.isDetailsNextEnabled, modifier = Modifier.padding(top = DesignToken.padding.small), diff --git a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/PreviewPage.kt b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/PreviewPage.kt index 30a7293c0d..882b37c112 100644 --- a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/PreviewPage.kt +++ b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/PreviewPage.kt @@ -64,7 +64,6 @@ import com.mifos.core.common.utils.CurrencyFormatter import com.mifos.core.designsystem.theme.DesignToken import com.mifos.core.designsystem.theme.MifosTypography import com.mifos.core.ui.components.MifosDefaultListingComponentFromStringResources -import com.mifos.core.ui.components.MifosProgressIndicatorOverlay import com.mifos.core.ui.components.MifosRowWithTextAndButton import com.mifos.core.ui.components.MifosTwoButtonRow import com.mifos.feature.loan.newLoanAccount.NewLoanAccountAction @@ -75,21 +74,6 @@ import org.jetbrains.compose.resources.stringResource fun PreviewPage( state: NewLoanAccountState, onAction: (NewLoanAccountAction) -> Unit, -) { - if (state.isLoading) { - MifosProgressIndicatorOverlay() - } else { - PreviewPageContent( - state, - onAction = onAction, - ) - } -} - -@Composable -fun PreviewPageContent( - state: NewLoanAccountState, - onAction: (NewLoanAccountAction) -> Unit, ) { Column( Modifier.fillMaxSize().padding(bottom = DesignToken.padding.large), @@ -131,7 +115,7 @@ fun PreviewPageContent( ) TermsCard( principal = CurrencyFormatter.format( - balance = state.principalAmount, + balance = state.principalAmount.toDouble(), currencyCode = state.loanTemplate?.currency?.code, maximumFractionDigits = 2, ), @@ -210,7 +194,7 @@ fun PreviewPageContent( modifier = Modifier.padding(top = DesignToken.padding.small), firstBtnText = stringResource(Res.string.back), secondBtnText = stringResource(Res.string.feature_loan_charge_submit), - onFirstBtnClick = { onAction(NewLoanAccountAction.GotoPreviousStep) }, + onFirstBtnClick = { onAction(NewLoanAccountAction.PreviousStep) }, onSecondBtnClick = { onAction(NewLoanAccountAction.SubmitLoanApplication) }, ) } diff --git a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/SchedulePage.kt b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/SchedulePage.kt index af66430b55..6936c1060f 100644 --- a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/SchedulePage.kt +++ b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/SchedulePage.kt @@ -29,7 +29,6 @@ import com.mifos.core.designsystem.theme.DesignToken import com.mifos.core.designsystem.theme.MifosTypography import com.mifos.core.model.objects.account.loan.Period import com.mifos.core.ui.components.MifosDefaultListingComponentFromStringResources -import com.mifos.core.ui.components.MifosProgressIndicator import com.mifos.core.ui.components.MifosTwoButtonRow import com.mifos.feature.loan.component.RepaymentPeriodCard import com.mifos.feature.loan.newLoanAccount.NewLoanAccountAction @@ -41,23 +40,6 @@ fun SchedulePage( state: NewLoanAccountState, onAction: (NewLoanAccountAction) -> Unit, modifier: Modifier = Modifier, -) { - if (state.isLoading) { - MifosProgressIndicator() - } else { - ScheduleContent( - state = state, - onAction = onAction, - modifier = modifier, - ) - } -} - -@Composable -fun ScheduleContent( - state: NewLoanAccountState, - onAction: (NewLoanAccountAction) -> Unit, - modifier: Modifier = Modifier, ) { Column( Modifier.fillMaxSize().padding(bottom = DesignToken.padding.large), diff --git a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/TermsPage.kt b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/TermsPage.kt index d1d9b74b6a..e1976694c9 100644 --- a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/TermsPage.kt +++ b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/TermsPage.kt @@ -21,13 +21,13 @@ import androidclient.feature.loan.generated.resources.collateral_data import androidclient.feature.loan.generated.resources.feature_loan_cancel import androidclient.feature.loan.generated.resources.feature_loan_select import androidclient.feature.loan.generated.resources.first_repayment_date -import androidclient.feature.loan.generated.resources.frequency import androidclient.feature.loan.generated.resources.grace_on_interest_payment import androidclient.feature.loan.generated.resources.grace_on_principal_payment import androidclient.feature.loan.generated.resources.interest_calculation_period import androidclient.feature.loan.generated.resources.interest_calculations import androidclient.feature.loan.generated.resources.interest_charged_from import androidclient.feature.loan.generated.resources.interest_free_period +import androidclient.feature.loan.generated.resources.interest_frequency import androidclient.feature.loan.generated.resources.interest_method import androidclient.feature.loan.generated.resources.is_equal_amortization import androidclient.feature.loan.generated.resources.loan_schedule @@ -44,6 +44,7 @@ import androidclient.feature.loan.generated.resources.principal import androidclient.feature.loan.generated.resources.recalculate_interest import androidclient.feature.loan.generated.resources.repaid_every import androidclient.feature.loan.generated.resources.repaid_every_label +import androidclient.feature.loan.generated.resources.repayment_frequency import androidclient.feature.loan.generated.resources.repayment_strategy import androidclient.feature.loan.generated.resources.repayments import androidclient.feature.loan.generated.resources.select_day @@ -122,7 +123,13 @@ fun TermsPage( onClick = { onAction(NewLoanAccountAction.OnFirstRepaymentDatePick(false)) firstRepaymentOnDatePickerState.selectedDateMillis?.let { - onAction(NewLoanAccountAction.OnFirstRepaymentDateChange(DateHelper.getDateAsStringFromLong(it))) + onAction( + NewLoanAccountAction.OnFirstRepaymentDateChange( + DateHelper.getDateAsStringFromLong( + it, + ), + ), + ) } }, ) { Text(stringResource(Res.string.feature_loan_select)) } @@ -149,7 +156,13 @@ fun TermsPage( onClick = { onAction(NewLoanAccountAction.OnInterestChargedFromDatePick(false)) interestChargedFromDatePickerState.selectedDateMillis?.let { - onAction(NewLoanAccountAction.OnInterestChargedFromChange(DateHelper.getDateAsStringFromLong(it))) + onAction( + NewLoanAccountAction.OnInterestChargedFromChange( + DateHelper.getDateAsStringFromLong( + it, + ), + ), + ) } }, ) { Text(stringResource(Res.string.feature_loan_select)) } @@ -178,9 +191,9 @@ fun TermsPage( Spacer(Modifier.height(DesignToken.padding.large)) MifosOutlinedTextField( - value = state.principalAmountText, + value = state.principalAmount, onValueChange = { - onAction(NewLoanAccountAction.OnPrincipalAmountChange(it.toDoubleOrNull() ?: 0.0, it)) + onAction(NewLoanAccountAction.OnPrincipalAmountChange(if (state.principalAmount.isEmpty()) "0" else it)) }, label = stringResource(Res.string.principal), config = MifosTextFieldConfig( @@ -213,13 +226,15 @@ fun TermsPage( value = if (state.termFrequencyIndex == -1) { "" } else { - state.loanTemplate?.termFrequencyTypeOptions[state.termFrequencyIndex]?.value ?: "" + state.loanTemplate?.termFrequencyTypeOptions[state.termFrequencyIndex]?.value + ?: "" }, onValueChanged = {}, onOptionSelected = { index, value -> onAction(NewLoanAccountAction.OnTermFrequencyIndexChange(index)) }, - options = state.loanTemplate?.termFrequencyTypeOptions?.map { it.value ?: "" } ?: emptyList(), + options = state.loanTemplate?.termFrequencyTypeOptions?.map { it.value ?: "" } + ?: emptyList(), label = stringResource(Res.string.term_frequency), ) @@ -277,12 +292,16 @@ fun TermsPage( value = if (state.termFrequencyIndex == -1) { "" } else { - state.loanTemplate?.termFrequencyTypeOptions[state.termFrequencyIndex]?.value ?: "" + state.loanTemplate?.termFrequencyTypeOptions[state.termFrequencyIndex]?.value + ?: "" }, onValueChanged = {}, - onOptionSelected = { index, value -> }, - options = state.loanTemplate?.termFrequencyTypeOptions?.map { it.value ?: "" } ?: emptyList(), - label = stringResource(Res.string.frequency), + onOptionSelected = { index, value -> + onAction(NewLoanAccountAction.OnTermFrequencyIndexChange(index)) + }, + options = state.loanTemplate?.termFrequencyTypeOptions?.map { it.value ?: "" } + ?: emptyList(), + label = stringResource(Res.string.repayment_frequency), readOnly = true, ) @@ -295,13 +314,16 @@ fun TermsPage( value = if (state.selectedOnIndex == -1) { "" } else { - state.loanTemplate?.repaymentFrequencyNthDayTypeOptions[state.selectedOnIndex]?.value ?: "" + state.loanTemplate?.repaymentFrequencyNthDayTypeOptions[state.selectedOnIndex]?.value + ?: "" }, onValueChanged = {}, onOptionSelected = { index, value -> onAction(NewLoanAccountAction.OnSelectedOnIndexChange(index)) }, - options = state.loanTemplate?.repaymentFrequencyNthDayTypeOptions?.map { it.value ?: "" } ?: emptyList(), + options = state.loanTemplate?.repaymentFrequencyNthDayTypeOptions?.map { + it.value ?: "" + } ?: emptyList(), label = stringResource(Res.string.select_on), ) @@ -309,13 +331,16 @@ fun TermsPage( value = if (state.selectedDayIndex == -1) { "" } else { - state.loanTemplate?.repaymentFrequencyDaysOfWeekTypeOptions[state.selectedDayIndex]?.value ?: "" + state.loanTemplate?.repaymentFrequencyDaysOfWeekTypeOptions[state.selectedDayIndex]?.value + ?: "" }, onValueChanged = {}, onOptionSelected = { index, value -> onAction(NewLoanAccountAction.OnSelectedDayIndexChange(index)) }, - options = state.loanTemplate?.repaymentFrequencyDaysOfWeekTypeOptions?.map { it.value ?: "" } ?: emptyList(), + options = state.loanTemplate?.repaymentFrequencyDaysOfWeekTypeOptions?.map { + it.value ?: "" + } ?: emptyList(), label = stringResource(Res.string.select_day), ) @@ -329,9 +354,13 @@ fun TermsPage( Spacer(Modifier.height(DesignToken.padding.large)) MifosOutlinedTextField( - value = state.nominalInterestRateText, + value = state.nominalInterestRate, onValueChange = { - onAction(NewLoanAccountAction.OnNominalInterestRateChange(it.toDoubleOrNull() ?: 0.0, it)) + onAction( + NewLoanAccountAction.OnNominalInterestRateChange( + if (state.nominalInterestRate.isBlank()) "0" else it, + ), + ) }, label = stringResource(Res.string.nominal_interest_rate_percent), config = MifosTextFieldConfig( @@ -347,27 +376,32 @@ fun TermsPage( value = if (state.nominalFrequencyIndex == -1) { "" } else { - state.loanTemplate?.interestRateFrequencyTypeOptions[state.nominalFrequencyIndex]?.value ?: "" + state.loanTemplate?.interestRateFrequencyTypeOptions[state.nominalFrequencyIndex]?.value + ?: "" }, onValueChanged = {}, onOptionSelected = { index, value -> onAction(NewLoanAccountAction.OnNominalFrequencyIndexChange(index)) }, - options = state.loanTemplate?.interestRateFrequencyTypeOptions?.map { it.value ?: "" } ?: emptyList(), - label = stringResource(Res.string.frequency), + options = state.loanTemplate?.interestRateFrequencyTypeOptions?.map { + it.value ?: "" + } ?: emptyList(), + label = stringResource(Res.string.interest_frequency), ) MifosTextFieldDropdown( value = if (state.nominalInterestMethodIndex == -1) { "" } else { - state.loanTemplate?.interestTypeOptions[state.nominalInterestMethodIndex]?.value ?: "" + state.loanTemplate?.interestTypeOptions[state.nominalInterestMethodIndex]?.value + ?: "" }, onValueChanged = {}, onOptionSelected = { index, value -> onAction(NewLoanAccountAction.OnNominalMethodIndexChange(index)) }, - options = state.loanTemplate?.interestTypeOptions?.map { it.value ?: "" } ?: emptyList(), + options = state.loanTemplate?.interestTypeOptions?.map { it.value ?: "" } + ?: emptyList(), label = stringResource(Res.string.interest_method), ) @@ -375,13 +409,15 @@ fun TermsPage( value = if (state.nominalAmortizationIndex == -1) { "" } else { - state.loanTemplate?.amortizationTypeOptions[state.nominalAmortizationIndex]?.value ?: "" + state.loanTemplate?.amortizationTypeOptions[state.nominalAmortizationIndex]?.value + ?: "" }, onValueChanged = {}, onOptionSelected = { index, value -> onAction(NewLoanAccountAction.OnNominalAmortizationIndexChange(index)) }, - options = state.loanTemplate?.amortizationTypeOptions?.map { it.value ?: "" } ?: emptyList(), + options = state.loanTemplate?.amortizationTypeOptions?.map { it.value ?: "" } + ?: emptyList(), label = stringResource(Res.string.amortization), ) @@ -427,20 +463,27 @@ fun TermsPage( value = if (state.repaymentStrategyIndex == -1) { "" } else { - state.loanTemplate?.transactionProcessingStrategyOptions[state.repaymentStrategyIndex]?.name ?: "" + state.loanTemplate?.transactionProcessingStrategyOptions[state.repaymentStrategyIndex]?.name + ?: "" }, onValueChanged = {}, onOptionSelected = { index, value -> onAction(NewLoanAccountAction.OnRepaymentStrategyIndexChange(index)) }, - options = state.loanTemplate?.transactionProcessingStrategyOptions?.map { it.name ?: "" } ?: emptyList(), + options = state.loanTemplate?.transactionProcessingStrategyOptions?.map { + it.name ?: "" + } ?: emptyList(), label = stringResource(Res.string.repayment_strategy), ) MifosOutlinedTextField( value = state.balloonRepaymentAmount.toString(), onValueChange = { - onAction(NewLoanAccountAction.OnBalloonRepaymentAmountChange(it.toIntOrNull() ?: 0)) + onAction( + NewLoanAccountAction.OnBalloonRepaymentAmountChange( + it.toIntOrNull() ?: 0, + ), + ) }, label = stringResource(Res.string.balloon_repayment_amount), config = MifosTextFieldConfig( @@ -463,13 +506,16 @@ fun TermsPage( value = if (state.interestCalculationPeriodIndex == -1) { "" } else { - state.loanTemplate?.interestCalculationPeriodTypeOptions[state.interestCalculationPeriodIndex]?.value ?: "" + state.loanTemplate?.interestCalculationPeriodTypeOptions[state.interestCalculationPeriodIndex]?.value + ?: "" }, onValueChanged = {}, onOptionSelected = { index, value -> onAction(NewLoanAccountAction.OnInterestCalculationPeriodIndexChange(index)) }, - options = state.loanTemplate?.interestCalculationPeriodTypeOptions?.map { it.value ?: "" } ?: emptyList(), + options = state.loanTemplate?.interestCalculationPeriodTypeOptions?.map { + it.value ?: "" + } ?: emptyList(), label = stringResource(Res.string.interest_calculation_period), ) @@ -532,7 +578,11 @@ fun TermsPage( MifosOutlinedTextField( value = state.moratoriumGraceOnPrincipalPayment.toString(), onValueChange = { - onAction(NewLoanAccountAction.OnMoratoriumGraceOnPrincipalPaymentChange(it.toIntOrNull() ?: 0)) + onAction( + NewLoanAccountAction.OnMoratoriumGraceOnPrincipalPaymentChange( + it.toIntOrNull() ?: 0, + ), + ) }, label = stringResource(Res.string.grace_on_principal_payment), config = MifosTextFieldConfig( @@ -547,7 +597,11 @@ fun TermsPage( MifosOutlinedTextField( value = state.moratoriumGraceOnInterestPayment.toString(), onValueChange = { - onAction(NewLoanAccountAction.OnMoratoriumGraceOnInterestPaymentChange(it.toIntOrNull() ?: 0)) + onAction( + NewLoanAccountAction.OnMoratoriumGraceOnInterestPaymentChange( + it.toIntOrNull() ?: 0, + ), + ) }, label = stringResource(Res.string.grace_on_interest_payment), config = MifosTextFieldConfig( @@ -562,7 +616,11 @@ fun TermsPage( MifosOutlinedTextField( value = state.moratoriumOnArrearsAgeing.toString(), onValueChange = { - onAction(NewLoanAccountAction.OnMoratoriumOnArrearsAgeingChange(it.toIntOrNull() ?: 0)) + onAction( + NewLoanAccountAction.OnMoratoriumOnArrearsAgeingChange( + it.toIntOrNull() ?: 0, + ), + ) }, label = stringResource(Res.string.on_arrears_ageing), config = MifosTextFieldConfig(