From bcc9dcc190edbc08cdded05f6d9ce61f5ef81051 Mon Sep 17 00:00:00 2001 From: Ankit kumar Date: Wed, 5 Nov 2025 17:44:19 +0530 Subject: [PATCH 1/3] merged development to mifo-557 --- .../data/repository/FixedDepositRepository.kt | 11 ++ .../FixedDepositRepositoryImpl.kt | 20 +++ .../com/mifos/room/basemodel/APIEndPoint.kt | 2 + .../com/mifos/core/network/BaseApiManager.kt | 3 + .../datamanager/DataManagerFixedDeposit.kt | 12 ++ .../mifos/core/network/di/DataMangerModule.kt | 2 + .../model/FixedDepositProductOption.kt | 20 +++ .../network/model/FixedDepositTemplate.kt | 18 ++ .../network/services/FixedDepositService.kt | 25 +++ .../composeResources/values/strings.xml | 6 + .../ClientApplyNewApplicationRoute.kt | 2 +- .../ClientApplyNewApplicationsScreen.kt | 4 +- .../mifos/feature/client/di/ClientModule.kt | 2 + .../client/navigation/ClientNavigation.kt | 9 +- .../FixedDepositAccountViewmodel.kt | 54 ------ ...n.kt => createFixedDepositAccountRoute.kt} | 20 ++- ....kt => createFixedDepositAccountScreen.kt} | 9 +- .../createFixedDepositAccountViewmodel.kt | 119 +++++++++++++ .../pages/DetailsPage.kt | 156 +++++++++++++++++- 19 files changed, 414 insertions(+), 80 deletions(-) create mode 100644 core/data/src/commonMain/kotlin/com/mifos/core/data/repository/FixedDepositRepository.kt create mode 100644 core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/FixedDepositRepositoryImpl.kt create mode 100644 core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerFixedDeposit.kt create mode 100644 core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositProductOption.kt create mode 100644 core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositTemplate.kt create mode 100644 core/network/src/commonMain/kotlin/com/mifos/core/network/services/FixedDepositService.kt delete mode 100644 feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/FixedDepositAccountViewmodel.kt rename feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/{FixedDepositAccountNavigation.kt => createFixedDepositAccountRoute.kt} (57%) rename feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/{FixedDepositAccountScreen.kt => createFixedDepositAccountScreen.kt} (94%) create mode 100644 feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountViewmodel.kt diff --git a/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/FixedDepositRepository.kt b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/FixedDepositRepository.kt new file mode 100644 index 00000000000..495116ef1bc --- /dev/null +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/FixedDepositRepository.kt @@ -0,0 +1,11 @@ +package com.mifos.core.data.repository + +import com.mifos.core.common.utils.DataState +import com.mifos.core.network.model.FixedDepositTemplate +import kotlinx.coroutines.flow.Flow + +interface FixedDepositRepository { + fun getFixedDepositTemplate(clientId: Int): Flow> + + +} \ No newline at end of file diff --git a/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/FixedDepositRepositoryImpl.kt b/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/FixedDepositRepositoryImpl.kt new file mode 100644 index 00000000000..cf4277feff0 --- /dev/null +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/FixedDepositRepositoryImpl.kt @@ -0,0 +1,20 @@ +package com.mifos.core.data.repositoryImp + + +import com.mifos.core.common.utils.DataState +import com.mifos.core.common.utils.asDataStateFlow +import com.mifos.core.data.repository.FixedDepositRepository +import com.mifos.core.network.datamanager.DataManagerFixedDeposit +import com.mifos.core.network.model.FixedDepositTemplate +import kotlinx.coroutines.flow.Flow + +class FixedDepositRepositoryImpl (private val dataManagerFixedDeposit: DataManagerFixedDeposit): FixedDepositRepository{ + + override fun getFixedDepositTemplate(clientId: Int): Flow> { + return dataManagerFixedDeposit.getFixedDepositTemplate(clientId).asDataStateFlow() + } + + + + +} diff --git a/core/database/src/commonMain/kotlin/com/mifos/room/basemodel/APIEndPoint.kt b/core/database/src/commonMain/kotlin/com/mifos/room/basemodel/APIEndPoint.kt index 7bae4faf831..d2b27dd47d3 100644 --- a/core/database/src/commonMain/kotlin/com/mifos/room/basemodel/APIEndPoint.kt +++ b/core/database/src/commonMain/kotlin/com/mifos/room/basemodel/APIEndPoint.kt @@ -41,4 +41,6 @@ object APIEndPoint { const val NOTES = "notes" const val MAKER_CHECKER = "makercheckers" const val SHARE = "share" + + const val FIXED_DEPOSIT ="fixeddepositaccounts" } diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/BaseApiManager.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/BaseApiManager.kt index d86f5eba518..dac3d167a83 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/BaseApiManager.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/BaseApiManager.kt @@ -44,6 +44,7 @@ import com.mifos.core.network.services.SearchService import com.mifos.core.network.services.ShareAccountService import com.mifos.core.network.services.StaffService import com.mifos.core.network.services.SurveyService +import com.mifos.core.network.services.FixedDepositService import com.mifos.core.network.services.createCenterService import com.mifos.core.network.services.createChargeService import com.mifos.core.network.services.createCheckerInboxService @@ -59,6 +60,7 @@ import com.mifos.core.network.services.createOfficeService import com.mifos.core.network.services.createRecurringAccountService import com.mifos.core.network.services.createRunReportsService import com.mifos.core.network.services.createSavingsAccountService +import com.mifos.core.network.services.createFixedDepositService import com.mifos.core.network.services.createSearchService import com.mifos.core.network.services.createShareAccountService import com.mifos.core.network.services.createStaffService @@ -76,6 +78,7 @@ class BaseApiManager( val dataTableService: DataTableService = ktorfit.createDataTableService() val loanService: LoanService = ktorfit.createLoanService() val savingsService: SavingsAccountService = ktorfit.createSavingsAccountService() + val fixedDepositService: FixedDepositService = ktorfit.createFixedDepositService() val recurringSavingsAccountService: RecurringAccountService = ktorfit.createRecurringAccountService() val searchService: SearchService = ktorfit.createSearchService() diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerFixedDeposit.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerFixedDeposit.kt new file mode 100644 index 00000000000..e10f45e3814 --- /dev/null +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerFixedDeposit.kt @@ -0,0 +1,12 @@ +package com.mifos.core.network.datamanager + +import com.mifos.core.network.BaseApiManager +import com.mifos.core.network.model.FixedDepositTemplate +import kotlinx.coroutines.flow.Flow + +class DataManagerFixedDeposit (private val baseApiManager: BaseApiManager){ + + fun getFixedDepositTemplate(clientId:Int): Flow = + baseApiManager.fixedDepositService.fixedDepositProductTemplate(clientId) + +} \ No newline at end of file diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt index 1a90f3c8ed6..0d398353f5c 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt @@ -21,6 +21,7 @@ import com.mifos.core.network.datamanager.DataManagerDataTable import com.mifos.core.network.datamanager.DataManagerDocument import com.mifos.core.network.datamanager.DataManagerGroups import com.mifos.core.network.datamanager.DataManagerIdentifiers +import com.mifos.core.network.datamanager.DataManagerFixedDeposit import com.mifos.core.network.datamanager.DataManagerLoan import com.mifos.core.network.datamanager.DataManagerNote import com.mifos.core.network.datamanager.DataManagerOffices @@ -55,4 +56,5 @@ val DataManagerModule = module { single { DataManagerIdentifiers(get()) } single { DataManagerRecurringAccount(get()) } single { DataManagerShare(get()) } + single { DataManagerFixedDeposit(get()) } } diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositProductOption.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositProductOption.kt new file mode 100644 index 00000000000..992a83fe979 --- /dev/null +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositProductOption.kt @@ -0,0 +1,20 @@ +package com.mifos.core.network.model + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class FixedDepositProductOption( + @SerialName("id") + val id: Int, + + @SerialName("name") + val name: String, + + @SerialName("shortName") + val shortName: String, + + @SerialName("totalShares") + val totalShares: Int, +) \ No newline at end of file diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositTemplate.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositTemplate.kt new file mode 100644 index 00000000000..88c0f9b860d --- /dev/null +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositTemplate.kt @@ -0,0 +1,18 @@ +package com.mifos.core.network.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + + + +@Serializable +data class FixedDepositTemplate( + @SerialName("clientId") + val clientId: Int, + + @SerialName(value = "clientName") + val clientName: String, + + @SerialName("productOptions") + val productOptions: List = emptyList(), +) \ No newline at end of file diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/services/FixedDepositService.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/services/FixedDepositService.kt new file mode 100644 index 00000000000..c1180ce6926 --- /dev/null +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/services/FixedDepositService.kt @@ -0,0 +1,25 @@ +package com.mifos.core.network.services + +import com.mifos.core.network.model.FixedDepositTemplate +import com.mifos.room.basemodel.APIEndPoint +import de.jensklingenberg.ktorfit.http.Query +import de.jensklingenberg.ktorfit.http.GET +import kotlinx.coroutines.flow.Flow + + + + + + +interface FixedDepositService { + + @GET("accounts/" + APIEndPoint.FIXED_DEPOSIT + "/template") + fun fixedDepositProductTemplate( + @Query("clientId") clientId: Int, + ): Flow +} + + + + + diff --git a/feature/client/src/commonMain/composeResources/values/strings.xml b/feature/client/src/commonMain/composeResources/values/strings.xml index 8fdc6ace0da..141d4034217 100644 --- a/feature/client/src/commonMain/composeResources/values/strings.xml +++ b/feature/client/src/commonMain/composeResources/values/strings.xml @@ -537,6 +537,12 @@ Update Document New Fixed Deposit Account + New Recurring Deposit Account + + 1 Year Fixed Deposit + Submission On + Field Officer + Details 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 e96b89390af..eb7842ec5c3 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 @@ -26,7 +26,7 @@ fun NavGraphBuilder.clientApplyNewApplicationRoute( onNavigateApplySavingsAccount: (Int) -> Unit, onNavigateApplyShareAccount: (Int) -> Unit, onNavigateApplyRecurringAccount: (Int) -> Unit, - onNavigateApplyFixedAccount: () -> Unit, + onNavigateApplyFixedAccount: (Int) -> Unit, navController: NavController, ) { composable { 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 1dc5624f953..06cf2304c92 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 @@ -63,7 +63,7 @@ internal fun ClientApplyNewApplicationsScreen( onNavigateApplySavingsAccount: (Int) -> Unit, onNavigateApplyShareAccount: (Int) -> Unit, onNavigateApplyRecurringAccount: (Int) -> Unit, - onNavigateApplyFixedAccount: () -> Unit, + onNavigateApplyFixedAccount: (Int) -> Unit, navController: NavController, viewModel: ClientApplyNewApplicationsViewModel = koinViewModel(), ) { @@ -74,7 +74,7 @@ internal fun ClientApplyNewApplicationsScreen( ClientApplyNewApplicationsEvent.NavigateBack -> onNavigateBack() is ClientApplyNewApplicationsEvent.OnActionClick -> { when (event.action) { - ClientApplyNewApplicationsItem.NewFixedAccount -> onNavigateApplyFixedAccount() + ClientApplyNewApplicationsItem.NewFixedAccount -> onNavigateApplyFixedAccount(state.clientId) ClientApplyNewApplicationsItem.NewLoanAccount -> onNavigateApplyLoanAccount( state.clientId, ) diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/di/ClientModule.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/di/ClientModule.kt index fa802bc0684..9c89912975c 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/di/ClientModule.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/di/ClientModule.kt @@ -40,6 +40,7 @@ import com.mifos.feature.client.createNewClient.CreateNewClientViewModel import com.mifos.feature.client.createShareAccount.CreateShareAccountViewModel import com.mifos.feature.client.documentPreviewScreen.DocumentPreviewScreenViewModel import com.mifos.feature.client.fixedDepositAccount.FixedDepositAccountViewModel +import com.mifos.feature.client.newFixedDepositAccount.CreateFixedDepositAccountViewmodel import com.mifos.feature.client.recurringDepositAccount.RecurringDepositAccountViewModel import com.mifos.feature.client.savingsAccounts.SavingsAccountsViewModel import com.mifos.feature.client.shareAccounts.ShareAccountsViewModel @@ -83,6 +84,7 @@ val ClientModule = module { viewModelOf(::ClientIdentifiersListViewModel) viewModelOf(::ClientIdentifiersAddUpdateViewModel) viewModelOf(::CreateShareAccountViewModel) + viewModelOf(::CreateFixedDepositAccountViewmodel) singleOf(::DocumentSelectAndUploadRepositoryImpl) { bind() diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/navigation/ClientNavigation.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/navigation/ClientNavigation.kt index 0ab69cc7483..0207ccc9b92 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/navigation/ClientNavigation.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/navigation/ClientNavigation.kt @@ -76,8 +76,9 @@ import com.mifos.feature.client.documentPreviewScreen.createDocumentPreviewRoute import com.mifos.feature.client.documentPreviewScreen.navigateToDocumentPreviewRoute import com.mifos.feature.client.fixedDepositAccount.clientFixedDepositAccountDestination import com.mifos.feature.client.fixedDepositAccount.navigateToFixedDepositAccountRoute -import com.mifos.feature.client.newFixedDepositAccount.fixedAccountDestination -import com.mifos.feature.client.newFixedDepositAccount.navigateToNewFixedDepositRoute +import com.mifos.feature.client.newFixedDepositAccount.createFixedDepositAccountRoute +import com.mifos.feature.client.newFixedDepositAccount.createFixedDepositAccountDestination +import com.mifos.feature.client.newFixedDepositAccount.navigateToCreateFixedDepositRoute import com.mifos.feature.client.recurringDepositAccount.clientRecurringDepositAccountDestination import com.mifos.feature.client.recurringDepositAccount.navigateToRecurringDepositAccountRoute import com.mifos.feature.client.savingsAccounts.navigateToClientSavingsAccountsRoute @@ -323,7 +324,7 @@ fun NavGraphBuilder.clientNavGraph( onNavigateApplyLoanAccount = navController::navigateToNewLoanAccountRoute, onNavigateApplySavingsAccount = navController::navigateToSavingsAccountRoute, onNavigateApplyRecurringAccount = navController::navigateToRecurringAccountRoute, - onNavigateApplyFixedAccount = navController::navigateToNewFixedDepositRoute, + onNavigateApplyFixedAccount = navController::navigateToCreateFixedDepositRoute, navController = navController, ) clientUpcomingChargesDestination( @@ -357,7 +358,7 @@ fun NavGraphBuilder.clientNavGraph( navController = navController, ) recurringAccountDestination(navController) - fixedAccountDestination() + createFixedDepositAccountDestination(navController = navController) } } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/FixedDepositAccountViewmodel.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/FixedDepositAccountViewmodel.kt deleted file mode 100644 index 3c047f1a0d4..00000000000 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/FixedDepositAccountViewmodel.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2025 Mifos Initiative - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * See https://github.com/openMF/android-client/blob/master/LICENSE.md - */ -package com.mifos.feature.client.newFixedDepositAccount - -import com.mifos.core.ui.util.BaseViewModel -import kotlinx.coroutines.flow.update - -class NewFixedDepositAccountViewmodel : - BaseViewModel< - NewFixedDepositAccountState, - NewFixedDepositAccountEvent, - NewFixedDepositAccountAction, - >(NewFixedDepositAccountState()) { - override fun handleAction(action: NewFixedDepositAccountAction) { - when (action) { - NewFixedDepositAccountAction.NextStep -> - mutableStateFlow.update { state -> - val maxIndex = 4 - state.copy(currentStep = (state.currentStep + 1).coerceAtMost(maxIndex)) - } - is NewFixedDepositAccountAction.OnStepChange -> { - mutableStateFlow.update { it.copy(currentStep = action.index) } - } - is NewFixedDepositAccountAction.NavigateBack -> { - sendEvent(NewFixedDepositAccountEvent.NavigateBack) - } - NewFixedDepositAccountAction.Finish -> { - sendEvent(NewFixedDepositAccountEvent.Finish) - } - } - } -} - -data class NewFixedDepositAccountState( - val currentStep: Int = 0, - val dialogState: Any? = null, -) -sealed class NewFixedDepositAccountAction() { - object NextStep : NewFixedDepositAccountAction() - data class OnStepChange(val index: Int) : NewFixedDepositAccountAction() - object NavigateBack : NewFixedDepositAccountAction() - object Finish : NewFixedDepositAccountAction() -} -sealed class NewFixedDepositAccountEvent() { - object NavigateBack : NewFixedDepositAccountEvent() - object Finish : NewFixedDepositAccountEvent() -} diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/FixedDepositAccountNavigation.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountRoute.kt similarity index 57% rename from feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/FixedDepositAccountNavigation.kt rename to feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountRoute.kt index 423e28e82df..7656af5fbf6 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/FixedDepositAccountNavigation.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountRoute.kt @@ -15,18 +15,24 @@ import androidx.navigation.compose.composable import kotlinx.serialization.Serializable @Serializable -data object FixedDepositRoute -fun NavGraphBuilder.fixedAccountDestination() { - composable { - FixedDepositAccountScreen( - onNavigateBack = {}, +data class createFixedDepositAccountRoute( + val clientId: Int, +) + + + +fun NavGraphBuilder.createFixedDepositAccountDestination(navController: NavController) { + composable { + CreateFixedDepositAccountScreen( + onNavigateBack = navController::popBackStack, onFinish = {}, + navController = navController, ) } } -fun NavController.navigateToNewFixedDepositRoute() { +fun NavController.navigateToCreateFixedDepositRoute(clientId: Int) { this.navigate( - FixedDepositRoute, + createFixedDepositAccountRoute(clientId=clientId) ) } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/FixedDepositAccountScreen.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountScreen.kt similarity index 94% rename from feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/FixedDepositAccountScreen.kt rename to feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountScreen.kt index 6b07e2d29a8..07c62a60b1d 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/FixedDepositAccountScreen.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountScreen.kt @@ -22,6 +22,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController import com.mifos.core.designsystem.component.MifosScaffold import com.mifos.core.ui.components.MifosStepper import com.mifos.core.ui.components.Step @@ -34,11 +35,12 @@ import com.mifos.feature.client.newFixedDepositAccount.pages.TermsPage import org.jetbrains.compose.resources.stringResource @Composable -internal fun FixedDepositAccountScreen( +internal fun CreateFixedDepositAccountScreen( onNavigateBack: () -> Unit, + navController: NavController, onFinish: () -> Unit, modifier: Modifier = Modifier, - viewModel: NewFixedDepositAccountViewmodel = viewModel(), + viewModel: CreateFixedDepositAccountViewmodel = viewModel(), ) { val state by viewModel.stateFlow.collectAsStateWithLifecycle() EventsEffect(viewModel.eventFlow) { event -> @@ -64,7 +66,8 @@ private fun FixedDepositAccountScaffold( listOf( Step(stringResource(Res.string.step_details)) { DetailsPage( - onNext = { onAction(NewFixedDepositAccountAction.NextStep) }, + state = state, + onAction = onAction, ) }, Step(name = stringResource(Res.string.step_terms)) { diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountViewmodel.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountViewmodel.kt new file mode 100644 index 00000000000..03c3b3dd7d4 --- /dev/null +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountViewmodel.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * See https://github.com/openMF/android-client/blob/master/LICENSE.md + */ +package com.mifos.feature.client.newFixedDepositAccount + +import com.mifos.core.common.utils.DateHelper +import com.mifos.core.ui.util.BaseViewModel +import com.mifos.room.entities.templates.clients.SavingProductOptionsEntity +import com.mifos.room.entities.templates.clients.StaffOptionsEntity +import kotlinx.coroutines.flow.update +import org.jetbrains.compose.resources.StringResource +import kotlin.time.Clock +import kotlin.time.ExperimentalTime + +class CreateFixedDepositAccountViewmodel : + BaseViewModel< + NewFixedDepositAccountState, + NewFixedDepositAccountEvent, + NewFixedDepositAccountAction, + >(NewFixedDepositAccountState()) { + override fun handleAction(action: NewFixedDepositAccountAction) { + when (action) { + is NewFixedDepositAccountAction.NextStep -> moveToNextStep() + is NewFixedDepositAccountAction.OnStepChange -> handleStepChange(action) + is NewFixedDepositAccountAction.NavigateBack -> sendEvent(NewFixedDepositAccountEvent.NavigateBack) + is NewFixedDepositAccountAction.Finish -> sendEvent(NewFixedDepositAccountEvent.Finish) + is NewFixedDepositAccountAction.OnSubmissionDatePick -> handleSubmissionDatePick(action) + is NewFixedDepositAccountAction.OnSubmissionDateChange -> handleSubmissionDateChange(action) + is NewFixedDepositAccountAction.OnProductNameChange -> handleOnProductNameChange(action) + is NewFixedDepositAccountAction.OnFieldOfficerChange -> handleFieldOfficerChange(action) + is NewFixedDepositAccountAction.OnExternalIdChange -> handleExternalIdChange(action) + NewFixedDepositAccountAction.OnDetailsSubmit -> handleOnDetailsSubmit() + } + } + private fun handleSubmissionDateChange(action: NewFixedDepositAccountAction.OnSubmissionDateChange) { + mutableStateFlow.update { it.copy(submissionDate = action.date) } + } + private fun handleStepChange(action: NewFixedDepositAccountAction.OnStepChange) { + mutableStateFlow.update { it.copy(currentStep = action.newIndex) } + } + + private fun handleSubmissionDatePick(action: NewFixedDepositAccountAction.OnSubmissionDatePick) { + mutableStateFlow.update { it.copy(showSubmissionDatePick = action.state) } + } + private fun handleOnProductNameChange(action: NewFixedDepositAccountAction.OnProductNameChange) { + mutableStateFlow.update { it.copy(fixedDepositProductSelected = action.index) } + } + private fun handleFieldOfficerChange(action: NewFixedDepositAccountAction.OnFieldOfficerChange) { + mutableStateFlow.update { it.copy(fieldOfficerIndex = action.index) } + } + private fun handleExternalIdChange(action: NewFixedDepositAccountAction.OnExternalIdChange) { + mutableStateFlow.update { it.copy(externalId = action.value) } + } + private fun handleOnDetailsSubmit() { + mutableStateFlow.update { + it.copy( + externalIdError = null, + ) + } + } + private fun moveToNextStep() { + val current = state.currentStep + if (current < state.totalSteps) { + mutableStateFlow.update { + it.copy( + currentStep = current + 1, + ) + } + } else { + sendEvent(NewFixedDepositAccountEvent.Finish) + } + } + + +} + +data class NewFixedDepositAccountState @OptIn(ExperimentalTime::class) constructor( + val currentStep: Int = 0, + val dialogState: Any? = null, + val showSubmissionDatePick: Boolean = false, + val fixedDepositProductSelected: Int = -1, + val fixedDepositProductOptions: List = emptyList(), + val submissionDate: String = DateHelper.getDateAsStringFromLong(Clock.System.now().toEpochMilliseconds()), + val fieldOfficerIndex: Int = -1, + val fieldOfficerOptions: List = emptyList(), + val externalId: String = "", + val externalIdError: StringResource? = null, + val totalSteps: Int = 4, + + +){ + val isDetailsNextEnabled = submissionDate.isNotEmpty() && + fixedDepositProductSelected != -1 && + fieldOfficerIndex != -1 +} +sealed class NewFixedDepositAccountAction() { + object NextStep : NewFixedDepositAccountAction() + data class OnStepChange(val newIndex: Int) : NewFixedDepositAccountAction() + object NavigateBack : NewFixedDepositAccountAction() + data class OnSubmissionDatePick(val state: Boolean) : NewFixedDepositAccountAction() + data class OnSubmissionDateChange(val date: String) : NewFixedDepositAccountAction() + data class OnProductNameChange(val index: Int) : NewFixedDepositAccountAction() + object Finish : NewFixedDepositAccountAction() + data class OnFieldOfficerChange(val index: Int) : NewFixedDepositAccountAction() + data class OnExternalIdChange(val value: String) : NewFixedDepositAccountAction() + data object OnDetailsSubmit : NewFixedDepositAccountAction() + + +} +sealed class NewFixedDepositAccountEvent() { + object NavigateBack : NewFixedDepositAccountEvent() + object Finish : NewFixedDepositAccountEvent() +} diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/DetailsPage.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/DetailsPage.kt index 99017367eec..be6fbadfb23 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/DetailsPage.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/DetailsPage.kt @@ -9,26 +9,164 @@ */ package com.mifos.feature.client.newFixedDepositAccount.pages +import androidclient.feature.client.generated.resources.Field_officer import androidclient.feature.client.generated.resources.Res +import androidclient.feature.client.generated.resources.btn_back + +import androidclient.feature.client.generated.resources.feature_client_next +import androidclient.feature.client.generated.resources.step_charges import androidclient.feature.client.generated.resources.step_details +import androidclient.feature.client.generated.resources.step_interest +import androidclient.feature.client.generated.resources.one_year_fixed_deposit +import androidclient.feature.client.generated.resources.submission_on + + import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height -import androidx.compose.material3.Button +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.DatePicker +import androidx.compose.material3.DatePickerDialog +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.SelectableDates import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.rememberDatePickerState import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp +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.ui.components.MifosTwoButtonRow +import com.mifos.feature.client.newFixedDepositAccount.NewFixedDepositAccountAction +import com.mifos.feature.client.newFixedDepositAccount.NewFixedDepositAccountState import org.jetbrains.compose.resources.stringResource +import kotlin.time.Clock +import kotlin.time.ExperimentalTime +@OptIn(ExperimentalMaterial3Api::class, ExperimentalTime::class) @Composable -fun DetailsPage(onNext: () -> Unit) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text(stringResource(Res.string.step_details)) - Spacer(Modifier.height(8.dp)) - Button(onClick = onNext) { - Text("Next Button") +fun DetailsPage( + state : NewFixedDepositAccountState, + onAction: (NewFixedDepositAccountAction) -> Unit, + modifier: Modifier = Modifier, + + ) { + val submissionDatePickerState = rememberDatePickerState( + initialSelectedDateMillis = Clock.System.now().toEpochMilliseconds(), + selectableDates = object : SelectableDates { + override fun isSelectableDate(utcTimeMillis: Long): Boolean { + return utcTimeMillis >= Clock.System.now().toEpochMilliseconds().minus(86_400_000L) + } + } + + ) + if (state.showSubmissionDatePick) { + DatePickerDialog( + onDismissRequest = { + onAction(NewFixedDepositAccountAction.OnSubmissionDatePick(state = false)) + }, + confirmButton = { + TextButton( + onClick = { + onAction(NewFixedDepositAccountAction.OnSubmissionDatePick(state = false)) + submissionDatePickerState.selectedDateMillis?.let { + onAction( + NewFixedDepositAccountAction.OnSubmissionDateChange( + DateHelper.getDateAsStringFromLong(it), + ), + ) + } + }, + ) { Text("Hello World") } + }, + dismissButton = { + TextButton( + onClick = { + onAction(NewFixedDepositAccountAction.OnSubmissionDatePick(state = false)) + }, + ) { Text("jljks") } + }, + ) { + DatePicker(state = submissionDatePickerState) + } + + } + Column (modifier = modifier.fillMaxSize()){ + Column( + modifier = modifier.weight(1f).verticalScroll(rememberScrollState()), + ) { + + MifosTextFieldDropdown( + value = if (state.fixedDepositProductSelected == -1) { + "" + } else { + state.fixedDepositProductOptions[state.fixedDepositProductSelected].name + }, + label = stringResource(Res.string.one_year_fixed_deposit), + onValueChanged = {}, + onOptionSelected = { index, value -> + onAction(NewFixedDepositAccountAction.OnProductNameChange(index)) + }, + options = state.fixedDepositProductOptions.map { + it.name + }, + + + + ) + MifosDatePickerTextField( + value = state.submissionDate, + label = stringResource(Res.string.submission_on), + openDatePicker = { + onAction(NewFixedDepositAccountAction.OnSubmissionDatePick(true)) + }, + ) + Spacer(Modifier.height(DesignToken.padding.large)) + MifosTextFieldDropdown( + value = if (state.fieldOfficerIndex == -1) { + "" + } else { + state.fieldOfficerOptions[state.fieldOfficerIndex].displayName + }, + label = stringResource(Res.string.Field_officer), + onValueChanged = {}, + onOptionSelected = { index, value -> + onAction(NewFixedDepositAccountAction.OnFieldOfficerChange(index)) + }, + options = state.fieldOfficerOptions.map { + it.displayName + } + + ) + MifosOutlinedTextField( + value = state.externalId, + onValueChange = { + onAction(NewFixedDepositAccountAction.OnExternalIdChange(it)) + }, + label = stringResource(Res.string.step_details), + config = MifosTextFieldConfig( + isError = state.externalIdError != null, + errorText = if (state.externalIdError != null) stringResource(state.externalIdError) else null, + ), + ) + Spacer(Modifier.height(DesignToken.padding.large)) } + MifosTwoButtonRow( + firstBtnText = stringResource(Res.string.btn_back), + secondBtnText = stringResource(Res.string.feature_client_next), + onFirstBtnClick = { onAction(NewFixedDepositAccountAction.NavigateBack) }, + onSecondBtnClick = { onAction(NewFixedDepositAccountAction.OnDetailsSubmit) }, + isSecondButtonEnabled = state.isDetailsNextEnabled, + modifier = Modifier.padding(top = DesignToken.padding.small), + ) + } + } From eb477fd0c545dc3c4c489d9eabfb260188bd8598 Mon Sep 17 00:00:00 2001 From: ANKIT KUMAR Date: Thu, 6 Nov 2025 19:40:35 +0530 Subject: [PATCH 2/3] Update code --- .../entities/accounts/savings/SavingAccountDepositTypeEntity.kt | 2 +- .../com/mifos/core/network/services/FixedDepositService.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/database/src/commonMain/kotlin/com/mifos/room/entities/accounts/savings/SavingAccountDepositTypeEntity.kt b/core/database/src/commonMain/kotlin/com/mifos/room/entities/accounts/savings/SavingAccountDepositTypeEntity.kt index e2d5de3f468..5ce7d5cdd6d 100644 --- a/core/database/src/commonMain/kotlin/com/mifos/room/entities/accounts/savings/SavingAccountDepositTypeEntity.kt +++ b/core/database/src/commonMain/kotlin/com/mifos/room/entities/accounts/savings/SavingAccountDepositTypeEntity.kt @@ -44,7 +44,7 @@ data class SavingAccountDepositTypeEntity( enum class ServerTypes(val id: Int, val code: String, val endpoint: String) { SAVINGS(100, "depositAccountType.savingsDeposit", APIEndPoint.SAVINGS_ACCOUNTS), - FIXED(200, "depositAccountType.fixedDeposit", APIEndPoint.SAVINGS_ACCOUNTS), + FIXED(200, "depositAccountType.fixedDeposit", APIEndPoint.FIXED_DEPOSIT), RECURRING(300, "depositAccountType.recurringDeposit", APIEndPoint.RECURRING_ACCOUNTS), ; diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/services/FixedDepositService.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/services/FixedDepositService.kt index c1180ce6926..42407c799bb 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/services/FixedDepositService.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/services/FixedDepositService.kt @@ -13,7 +13,7 @@ import kotlinx.coroutines.flow.Flow interface FixedDepositService { - @GET("accounts/" + APIEndPoint.FIXED_DEPOSIT + "/template") + @GET( APIEndPoint.FIXED_DEPOSIT + "/template") fun fixedDepositProductTemplate( @Query("clientId") clientId: Int, ): Flow From 8b99dfbb343e0f5a04c541ae5d7c81ca212ac93d Mon Sep 17 00:00:00 2001 From: shashank-0-0 Date: Mon, 17 Nov 2025 21:55:19 +0530 Subject: [PATCH 3/3] implemented fixedDepositAccounts details page --- .../mifos/core/data/di/RepositoryModule.kt | 3 + .../data/repository/FixedDepositRepository.kt | 5 +- .../FixedDepositRepositoryImpl.kt | 4 +- .../datamanager/DataManagerFixedDeposit.kt | 4 +- .../model/FixedDepositProductOption.kt | 11 +- .../network/model/FixedDepositTemplate.kt | 12 +- .../network/services/FixedDepositService.kt | 1 + .../createFixedDepositAccountScreen.kt | 62 ++++-- .../createFixedDepositAccountViewmodel.kt | 187 +++++++++++++++--- .../pages/DetailsPage.kt | 82 ++++---- 10 files changed, 272 insertions(+), 99 deletions(-) diff --git a/core/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.kt b/core/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.kt index 03892f317c0..e9e46c0d3ab 100644 --- a/core/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.kt +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.kt @@ -29,6 +29,7 @@ import com.mifos.core.data.repository.DataTableRepository import com.mifos.core.data.repository.DataTableRowDialogRepository import com.mifos.core.data.repository.DocumentCreateUpdateRepository import com.mifos.core.data.repository.DocumentListRepository +import com.mifos.core.data.repository.FixedDepositRepository import com.mifos.core.data.repository.GenerateCollectionSheetRepository import com.mifos.core.data.repository.GroupDetailsRepository import com.mifos.core.data.repository.GroupListRepository @@ -91,6 +92,7 @@ import com.mifos.core.data.repositoryImp.DataTableRepositoryImp import com.mifos.core.data.repositoryImp.DataTableRowDialogRepositoryImp import com.mifos.core.data.repositoryImp.DocumentCreateUpdateRepositoryImp import com.mifos.core.data.repositoryImp.DocumentListRepositoryImp +import com.mifos.core.data.repositoryImp.FixedDepositRepositoryImpl import com.mifos.core.data.repositoryImp.GenerateCollectionSheetRepositoryImp import com.mifos.core.data.repositoryImp.GroupDetailsRepositoryImp import com.mifos.core.data.repositoryImp.GroupListRepositoryImp @@ -223,6 +225,7 @@ val RepositoryModule = module { singleOf(::RecurringAccountRepositoryImp) bind RecurringAccountRepository::class singleOf(::ShareAccountRepositoryImpl) bind ShareAccountRepository::class + singleOf(::FixedDepositRepositoryImpl) bind FixedDepositRepository::class includes(platformModule) single { getPlatformDataModule } diff --git a/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/FixedDepositRepository.kt b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/FixedDepositRepository.kt index 495116ef1bc..718586da33a 100644 --- a/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/FixedDepositRepository.kt +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/FixedDepositRepository.kt @@ -5,7 +5,10 @@ import com.mifos.core.network.model.FixedDepositTemplate import kotlinx.coroutines.flow.Flow interface FixedDepositRepository { - fun getFixedDepositTemplate(clientId: Int): Flow> + fun getFixedDepositTemplate( + clientId: Int, + productId: Int? + ): Flow> } \ No newline at end of file diff --git a/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/FixedDepositRepositoryImpl.kt b/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/FixedDepositRepositoryImpl.kt index cf4277feff0..e1882e9b8bd 100644 --- a/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/FixedDepositRepositoryImpl.kt +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/FixedDepositRepositoryImpl.kt @@ -10,8 +10,8 @@ import kotlinx.coroutines.flow.Flow class FixedDepositRepositoryImpl (private val dataManagerFixedDeposit: DataManagerFixedDeposit): FixedDepositRepository{ - override fun getFixedDepositTemplate(clientId: Int): Flow> { - return dataManagerFixedDeposit.getFixedDepositTemplate(clientId).asDataStateFlow() + override fun getFixedDepositTemplate(clientId: Int,productId: Int?): Flow> { + return dataManagerFixedDeposit.getFixedDepositTemplate(clientId,productId).asDataStateFlow() } diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerFixedDeposit.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerFixedDeposit.kt index e10f45e3814..8a7847edc9b 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerFixedDeposit.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerFixedDeposit.kt @@ -6,7 +6,7 @@ import kotlinx.coroutines.flow.Flow class DataManagerFixedDeposit (private val baseApiManager: BaseApiManager){ - fun getFixedDepositTemplate(clientId:Int): Flow = - baseApiManager.fixedDepositService.fixedDepositProductTemplate(clientId) + fun getFixedDepositTemplate(clientId:Int,productId: Int?): Flow = + baseApiManager.fixedDepositService.fixedDepositProductTemplate(clientId,productId) } \ No newline at end of file diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositProductOption.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositProductOption.kt index 992a83fe979..0f84440d742 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositProductOption.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositProductOption.kt @@ -7,14 +7,11 @@ import kotlinx.serialization.Serializable @Serializable data class FixedDepositProductOption( @SerialName("id") - val id: Int, + val id: Int? = null, @SerialName("name") - val name: String, + val name: String? = null, - @SerialName("shortName") - val shortName: String, - - @SerialName("totalShares") - val totalShares: Int, + @SerialName("withHoldTax") + val withHoldTax: Boolean? = null, ) \ No newline at end of file diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositTemplate.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositTemplate.kt index 88c0f9b860d..5c25f8fb704 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositTemplate.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositTemplate.kt @@ -1,5 +1,7 @@ package com.mifos.core.network.model +import com.mifos.core.model.objects.account.saving.FieldOfficerOptions +import com.mifos.core.model.utils.IgnoredOnParcel import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -8,11 +10,15 @@ import kotlinx.serialization.Serializable @Serializable data class FixedDepositTemplate( @SerialName("clientId") - val clientId: Int, + val clientId: Int? = null, @SerialName(value = "clientName") - val clientName: String, + val clientName: String? = null, @SerialName("productOptions") - val productOptions: List = emptyList(), + val productOptions: List? = null, + + @SerialName("fieldOfficerOptions") + val fieldOfficerOptions: List? = null, + ) \ No newline at end of file diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/services/FixedDepositService.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/services/FixedDepositService.kt index 42407c799bb..cfa5ce0fccb 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/services/FixedDepositService.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/services/FixedDepositService.kt @@ -16,6 +16,7 @@ interface FixedDepositService { @GET( APIEndPoint.FIXED_DEPOSIT + "/template") fun fixedDepositProductTemplate( @Query("clientId") clientId: Int, + @Query("productId") productId: Int?, ): Flow } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountScreen.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountScreen.kt index 07c62a60b1d..6ef231cb054 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountScreen.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountScreen.kt @@ -16,6 +16,7 @@ import androidclient.feature.client.generated.resources.step_interest import androidclient.feature.client.generated.resources.step_settings import androidclient.feature.client.generated.resources.step_terms import androidclient.feature.client.generated.resources.title_new_fixed_deposit_account +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -24,6 +25,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import com.mifos.core.designsystem.component.MifosScaffold +import com.mifos.core.ui.components.MifosBreadcrumbNavBar +import com.mifos.core.ui.components.MifosErrorComponent +import com.mifos.core.ui.components.MifosProgressIndicator import com.mifos.core.ui.components.MifosStepper import com.mifos.core.ui.components.Step import com.mifos.core.ui.util.EventsEffect @@ -33,6 +37,7 @@ import com.mifos.feature.client.newFixedDepositAccount.pages.InterestPage import com.mifos.feature.client.newFixedDepositAccount.pages.SettingPage import com.mifos.feature.client.newFixedDepositAccount.pages.TermsPage import org.jetbrains.compose.resources.stringResource +import org.koin.compose.viewmodel.koinViewModel @Composable internal fun CreateFixedDepositAccountScreen( @@ -40,7 +45,7 @@ internal fun CreateFixedDepositAccountScreen( navController: NavController, onFinish: () -> Unit, modifier: Modifier = Modifier, - viewModel: CreateFixedDepositAccountViewmodel = viewModel(), + viewModel: CreateFixedDepositAccountViewmodel = koinViewModel(), ) { val state by viewModel.stateFlow.collectAsStateWithLifecycle() EventsEffect(viewModel.eventFlow) { event -> @@ -50,7 +55,8 @@ internal fun CreateFixedDepositAccountScreen( } } FixedDepositAccountScaffold( - state = state, + navController = navController, + newFixedDepositAccountState = state, onAction = { viewModel.trySendAction(it) }, modifier = modifier, ) @@ -58,7 +64,8 @@ internal fun CreateFixedDepositAccountScreen( @Composable private fun FixedDepositAccountScaffold( - state: NewFixedDepositAccountState, + navController: NavController, + newFixedDepositAccountState: NewFixedDepositAccountState, onAction: (NewFixedDepositAccountAction) -> Unit, modifier: Modifier = Modifier, ) { @@ -66,29 +73,29 @@ private fun FixedDepositAccountScaffold( listOf( Step(stringResource(Res.string.step_details)) { DetailsPage( - state = state, + state = newFixedDepositAccountState, onAction = onAction, ) }, Step(name = stringResource(Res.string.step_terms)) { TermsPage( - onNext = { onAction(NewFixedDepositAccountAction.NextStep) }, + onNext = { onAction(NewFixedDepositAccountAction.OnNextPress) }, ) }, Step(name = stringResource(Res.string.step_settings)) { SettingPage( - onNext = { onAction(NewFixedDepositAccountAction.NextStep) }, + onNext = { onAction(NewFixedDepositAccountAction.OnNextPress) }, ) }, Step(name = stringResource(Res.string.step_interest)) { InterestPage( - onNext = { onAction(NewFixedDepositAccountAction.NextStep) }, + onNext = { onAction(NewFixedDepositAccountAction.OnNextPress) }, ) }, Step(stringResource(Res.string.step_charges)) { ChargesPage( - onNext = { onAction(NewFixedDepositAccountAction.NextStep) }, + onNext = { onAction(NewFixedDepositAccountAction.OnNextPress) }, ) }, ) @@ -99,17 +106,36 @@ private fun FixedDepositAccountScaffold( modifier = modifier, ) { paddingValues -> - if (state.dialogState == null) { - MifosStepper( - steps = steps, - currentIndex = state.currentStep, - onStepChange = { newIndex -> - onAction(NewFixedDepositAccountAction.OnStepChange(newIndex)) - }, - modifier = Modifier - .fillMaxWidth(), + Column { + MifosBreadcrumbNavBar(navController) + when (newFixedDepositAccountState.screenState) { + is NewFixedDepositAccountState.ScreenState.Error -> { + MifosErrorComponent( + message = newFixedDepositAccountState.screenState.message, + isRetryEnabled = true, + ) { + onAction(NewFixedDepositAccountAction.Retry) + } + } + + is NewFixedDepositAccountState.ScreenState.Loading -> { + MifosProgressIndicator() + } - ) + is NewFixedDepositAccountState.ScreenState.Success -> { + MifosStepper( + steps = steps, + currentIndex = newFixedDepositAccountState.currentStep, + onStepChange = { newIndex -> + onAction(NewFixedDepositAccountAction.OnStepChange(newIndex)) + }, + modifier = Modifier + .fillMaxWidth(), + + ) + } + } } + } } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountViewmodel.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountViewmodel.kt index 03c3b3dd7d4..81c39d9183f 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountViewmodel.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountViewmodel.kt @@ -9,24 +9,44 @@ */ package com.mifos.feature.client.newFixedDepositAccount +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.viewModelScope +import androidx.navigation.toRoute +import com.mifos.core.common.utils.DataState import com.mifos.core.common.utils.DateHelper +import com.mifos.core.data.repository.FixedDepositRepository +import com.mifos.core.model.objects.template.recurring.FieldOfficerOption +import com.mifos.core.network.model.FixedDepositTemplate import com.mifos.core.ui.util.BaseViewModel -import com.mifos.room.entities.templates.clients.SavingProductOptionsEntity -import com.mifos.room.entities.templates.clients.StaffOptionsEntity +import com.mifos.feature.client.fixedDepositAccount.FixedDepositAccountRoute import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch import org.jetbrains.compose.resources.StringResource import kotlin.time.Clock import kotlin.time.ExperimentalTime -class CreateFixedDepositAccountViewmodel : +class CreateFixedDepositAccountViewmodel( + savedStateHandle: SavedStateHandle, + private val fixedDepositRepository: FixedDepositRepository, +) : BaseViewModel< - NewFixedDepositAccountState, - NewFixedDepositAccountEvent, - NewFixedDepositAccountAction, - >(NewFixedDepositAccountState()) { + NewFixedDepositAccountState, + NewFixedDepositAccountEvent, + NewFixedDepositAccountAction, + >( + NewFixedDepositAccountState( + clientId = savedStateHandle.toRoute().clientId, + ), + ) { + + + init { + loadFixedDepositAccountTemplate() + } + override fun handleAction(action: NewFixedDepositAccountAction) { when (action) { - is NewFixedDepositAccountAction.NextStep -> moveToNextStep() + is NewFixedDepositAccountAction.OnNextPress -> moveToNextStep() is NewFixedDepositAccountAction.OnStepChange -> handleStepChange(action) is NewFixedDepositAccountAction.NavigateBack -> sendEvent(NewFixedDepositAccountEvent.NavigateBack) is NewFixedDepositAccountAction.Finish -> sendEvent(NewFixedDepositAccountEvent.Finish) @@ -36,34 +56,128 @@ class CreateFixedDepositAccountViewmodel : is NewFixedDepositAccountAction.OnFieldOfficerChange -> handleFieldOfficerChange(action) is NewFixedDepositAccountAction.OnExternalIdChange -> handleExternalIdChange(action) NewFixedDepositAccountAction.OnDetailsSubmit -> handleOnDetailsSubmit() + NewFixedDepositAccountAction.Retry -> handleRetry() + } + } + + private fun handleRetry(){ + loadFixedDepositAccountTemplate() + } + + + private fun loadFixedDepositAccountTemplate() = viewModelScope.launch { + + fixedDepositRepository.getFixedDepositTemplate( + clientId = state.clientId, + productId = state.template.productOptions?.get(state.fixedDepositAccountDetail.productSelected)?.id, + ).collect { state -> + when (state) { + is DataState.Success -> { + setSuccessState() + mutableStateFlow.update { + it.copy( + template = state.data, + ) + } + } + + is DataState.Error -> { + setErrorState(state.message) + } + + DataState.Loading -> { + setLoadingState() + } + } + } + } + + private fun setLoadingState() { + mutableStateFlow.update { + it.copy( + screenState = NewFixedDepositAccountState.ScreenState.Loading, + ) } } + + private fun setSuccessState() { + mutableStateFlow.update { + it.copy(screenState = NewFixedDepositAccountState.ScreenState.Success) + } + } + private fun setErrorState(message: String) { + mutableStateFlow.update { + it.copy( + screenState = NewFixedDepositAccountState.ScreenState.Error(message), + ) + } + } + private fun handleSubmissionDateChange(action: NewFixedDepositAccountAction.OnSubmissionDateChange) { - mutableStateFlow.update { it.copy(submissionDate = action.date) } + mutableStateFlow.update { + it.copy( + fixedDepositAccountDetail = it.fixedDepositAccountDetail.copy( + submissionDate = action.date + ) + ) + } } + private fun handleStepChange(action: NewFixedDepositAccountAction.OnStepChange) { mutableStateFlow.update { it.copy(currentStep = action.newIndex) } } private fun handleSubmissionDatePick(action: NewFixedDepositAccountAction.OnSubmissionDatePick) { - mutableStateFlow.update { it.copy(showSubmissionDatePick = action.state) } + mutableStateFlow.update { + it.copy( + fixedDepositAccountDetail = it.fixedDepositAccountDetail.copy( + showSubmissionDatePick = action.state, + ), + ) + } } + private fun handleOnProductNameChange(action: NewFixedDepositAccountAction.OnProductNameChange) { - mutableStateFlow.update { it.copy(fixedDepositProductSelected = action.index) } + mutableStateFlow.update { + it.copy( + fixedDepositAccountDetail = it.fixedDepositAccountDetail.copy( + productSelected = action.index, + ), + ) + } + loadFixedDepositAccountTemplate() } + private fun handleFieldOfficerChange(action: NewFixedDepositAccountAction.OnFieldOfficerChange) { - mutableStateFlow.update { it.copy(fieldOfficerIndex = action.index) } + mutableStateFlow.update { + it.copy( + fixedDepositAccountDetail = it.fixedDepositAccountDetail.copy( + fieldOfficerIndex = action.index, + ), + ) + } } + private fun handleExternalIdChange(action: NewFixedDepositAccountAction.OnExternalIdChange) { - mutableStateFlow.update { it.copy(externalId = action.value) } + mutableStateFlow.update { + it.copy( + fixedDepositAccountDetail = it.fixedDepositAccountDetail.copy( + externalId = action.value, + ), + ) + } } + private fun handleOnDetailsSubmit() { mutableStateFlow.update { it.copy( - externalIdError = null, + fixedDepositAccountDetail = it.fixedDepositAccountDetail.copy( + externalIdError = null, + ), ) } } + private fun moveToNextStep() { val current = state.currentStep if (current < state.totalSteps) { @@ -80,28 +194,44 @@ class CreateFixedDepositAccountViewmodel : } -data class NewFixedDepositAccountState @OptIn(ExperimentalTime::class) constructor( +data class NewFixedDepositAccountState constructor( + val clientId: Int = -1, val currentStep: Int = 0, val dialogState: Any? = null, + val totalSteps: Int = 4, + val screenState: ScreenState = ScreenState.Loading, + val fixedDepositAccountDetail: FixedDepositAccountDetailsState = FixedDepositAccountDetailsState(), + val template: FixedDepositTemplate = FixedDepositTemplate() +) { + sealed interface ScreenState { + data class Error(val message: String) : ScreenState + data object Loading : ScreenState + data object Success : ScreenState + } +} + +data class FixedDepositAccountDetailsState @OptIn(ExperimentalTime::class) constructor( + val submittedOnDate: String = "", + val fieldOfficer: FieldOfficerOption? = null, val showSubmissionDatePick: Boolean = false, - val fixedDepositProductSelected: Int = -1, - val fixedDepositProductOptions: List = emptyList(), - val submissionDate: String = DateHelper.getDateAsStringFromLong(Clock.System.now().toEpochMilliseconds()), + val productSelected: Int = -1, + val submissionDate: String = DateHelper.getDateAsStringFromLong( + Clock.System.now().toEpochMilliseconds(), + ), val fieldOfficerIndex: Int = -1, - val fieldOfficerOptions: List = emptyList(), + val fieldOfficerError: String? = null, val externalId: String = "", val externalIdError: StringResource? = null, - val totalSteps: Int = 4, - - -){ - val isDetailsNextEnabled = submissionDate.isNotEmpty() && - fixedDepositProductSelected != -1 && - fieldOfficerIndex != -1 + val isMiniLoaderActive: Boolean = false, + val fieldOfficerOptions: List? = null, +) { + val isDetailsNextEnabled = submissionDate.isNotEmpty() && fieldOfficerIndex != -1 } + sealed class NewFixedDepositAccountAction() { - object NextStep : NewFixedDepositAccountAction() + object OnNextPress : NewFixedDepositAccountAction() data class OnStepChange(val newIndex: Int) : NewFixedDepositAccountAction() + object NavigateBack : NewFixedDepositAccountAction() data class OnSubmissionDatePick(val state: Boolean) : NewFixedDepositAccountAction() data class OnSubmissionDateChange(val date: String) : NewFixedDepositAccountAction() @@ -111,8 +241,11 @@ sealed class NewFixedDepositAccountAction() { data class OnExternalIdChange(val value: String) : NewFixedDepositAccountAction() data object OnDetailsSubmit : NewFixedDepositAccountAction() + data object Retry : NewFixedDepositAccountAction() + } + sealed class NewFixedDepositAccountEvent() { object NavigateBack : NewFixedDepositAccountEvent() object Finish : NewFixedDepositAccountEvent() diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/DetailsPage.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/DetailsPage.kt index be6fbadfb23..9e1c35ca6ff 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/DetailsPage.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/DetailsPage.kt @@ -12,6 +12,9 @@ package com.mifos.feature.client.newFixedDepositAccount.pages import androidclient.feature.client.generated.resources.Field_officer import androidclient.feature.client.generated.resources.Res import androidclient.feature.client.generated.resources.btn_back +import androidclient.feature.client.generated.resources.feature_client_charge_cancel +import androidclient.feature.client.generated.resources.feature_client_charge_select +import androidclient.feature.client.generated.resources.feature_client_external_id import androidclient.feature.client.generated.resources.feature_client_next import androidclient.feature.client.generated.resources.step_charges @@ -53,7 +56,7 @@ import kotlin.time.ExperimentalTime @OptIn(ExperimentalMaterial3Api::class, ExperimentalTime::class) @Composable fun DetailsPage( - state : NewFixedDepositAccountState, + state: NewFixedDepositAccountState, onAction: (NewFixedDepositAccountAction) -> Unit, modifier: Modifier = Modifier, @@ -64,10 +67,10 @@ fun DetailsPage( override fun isSelectableDate(utcTimeMillis: Long): Boolean { return utcTimeMillis >= Clock.System.now().toEpochMilliseconds().minus(86_400_000L) } - } + }, - ) - if (state.showSubmissionDatePick) { + ) + if (state.fixedDepositAccountDetail.showSubmissionDatePick) { DatePickerDialog( onDismissRequest = { onAction(NewFixedDepositAccountAction.OnSubmissionDatePick(state = false)) @@ -84,45 +87,46 @@ fun DetailsPage( ) } }, - ) { Text("Hello World") } + ) { Text(stringResource(Res.string.feature_client_charge_select)) } }, dismissButton = { TextButton( onClick = { onAction(NewFixedDepositAccountAction.OnSubmissionDatePick(state = false)) }, - ) { Text("jljks") } + ) { Text(stringResource(Res.string.feature_client_charge_cancel)) } }, ) { DatePicker(state = submissionDatePickerState) } } - Column (modifier = modifier.fillMaxSize()){ - Column( - modifier = modifier.weight(1f).verticalScroll(rememberScrollState()), - ) { - - MifosTextFieldDropdown( - value = if (state.fixedDepositProductSelected == -1) { - "" - } else { - state.fixedDepositProductOptions[state.fixedDepositProductSelected].name - }, - label = stringResource(Res.string.one_year_fixed_deposit), - onValueChanged = {}, - onOptionSelected = { index, value -> - onAction(NewFixedDepositAccountAction.OnProductNameChange(index)) - }, - options = state.fixedDepositProductOptions.map { - it.name - }, + Column( + modifier = modifier.fillMaxSize() + .verticalScroll(rememberScrollState()), + ) { + MifosTextFieldDropdown( + value = if (state.fixedDepositAccountDetail.productSelected == -1) { + "" + } else { + state.template.productOptions?.get(state.fixedDepositAccountDetail.productSelected)?.name ?: "" + }, + label = stringResource(Res.string.one_year_fixed_deposit), + onValueChanged = {}, + onOptionSelected = { index, value -> + onAction(NewFixedDepositAccountAction.OnProductNameChange(index)) + }, + options = state.template.productOptions?.map { + it.name ?: "" + } ?: emptyList(), ) + + if (!state.template.fieldOfficerOptions.isNullOrEmpty()) { MifosDatePickerTextField( - value = state.submissionDate, + value = state.fixedDepositAccountDetail.submissionDate, label = stringResource(Res.string.submission_on), openDatePicker = { onAction(NewFixedDepositAccountAction.OnSubmissionDatePick(true)) @@ -130,30 +134,31 @@ fun DetailsPage( ) Spacer(Modifier.height(DesignToken.padding.large)) MifosTextFieldDropdown( - value = if (state.fieldOfficerIndex == -1) { + value = if (state.fixedDepositAccountDetail.fieldOfficerIndex == -1) { "" } else { - state.fieldOfficerOptions[state.fieldOfficerIndex].displayName + state.template.fieldOfficerOptions?.get(state.fixedDepositAccountDetail.fieldOfficerIndex)?.displayName + ?: "" }, label = stringResource(Res.string.Field_officer), onValueChanged = {}, onOptionSelected = { index, value -> onAction(NewFixedDepositAccountAction.OnFieldOfficerChange(index)) }, - options = state.fieldOfficerOptions.map { - it.displayName - } + options = state.template.fieldOfficerOptions?.map { + it.displayName ?: "" + } ?: emptyList(), - ) + ) MifosOutlinedTextField( - value = state.externalId, + value = state.fixedDepositAccountDetail.externalId, onValueChange = { onAction(NewFixedDepositAccountAction.OnExternalIdChange(it)) }, - label = stringResource(Res.string.step_details), + label = stringResource(Res.string.feature_client_external_id), config = MifosTextFieldConfig( - isError = state.externalIdError != null, - errorText = if (state.externalIdError != null) stringResource(state.externalIdError) else null, + isError = state.fixedDepositAccountDetail.externalIdError != null, + errorText = if (state.fixedDepositAccountDetail.externalIdError != null) stringResource(state.fixedDepositAccountDetail.externalIdError) else null, ), ) Spacer(Modifier.height(DesignToken.padding.large)) @@ -162,11 +167,10 @@ fun DetailsPage( firstBtnText = stringResource(Res.string.btn_back), secondBtnText = stringResource(Res.string.feature_client_next), onFirstBtnClick = { onAction(NewFixedDepositAccountAction.NavigateBack) }, - onSecondBtnClick = { onAction(NewFixedDepositAccountAction.OnDetailsSubmit) }, - isSecondButtonEnabled = state.isDetailsNextEnabled, + onSecondBtnClick = { onAction(NewFixedDepositAccountAction.OnNextPress) }, + isSecondButtonEnabled = state.fixedDepositAccountDetail.isDetailsNextEnabled, modifier = Modifier.padding(top = DesignToken.padding.small), ) - } }