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 new file mode 100644 index 00000000000..718586da33a --- /dev/null +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/FixedDepositRepository.kt @@ -0,0 +1,14 @@ +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, + 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 new file mode 100644 index 00000000000..e1882e9b8bd --- /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,productId: Int?): Flow> { + return dataManagerFixedDeposit.getFixedDepositTemplate(clientId,productId).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/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/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..8a7847edc9b --- /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,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/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..0f84440d742 --- /dev/null +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositProductOption.kt @@ -0,0 +1,17 @@ +package com.mifos.core.network.model + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class FixedDepositProductOption( + @SerialName("id") + val id: Int? = null, + + @SerialName("name") + val name: String? = null, + + @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 new file mode 100644 index 00000000000..5c25f8fb704 --- /dev/null +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositTemplate.kt @@ -0,0 +1,24 @@ +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 + + + +@Serializable +data class FixedDepositTemplate( + @SerialName("clientId") + val clientId: Int? = null, + + @SerialName(value = "clientName") + val clientName: String? = null, + + @SerialName("productOptions") + 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 new file mode 100644 index 00000000000..cfa5ce0fccb --- /dev/null +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/services/FixedDepositService.kt @@ -0,0 +1,26 @@ +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( APIEndPoint.FIXED_DEPOSIT + "/template") + fun fixedDepositProductTemplate( + @Query("clientId") clientId: Int, + @Query("productId") productId: 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 64% 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..6ef231cb054 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 @@ -16,13 +16,18 @@ 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 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.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 @@ -32,13 +37,15 @@ 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 FixedDepositAccountScreen( +internal fun CreateFixedDepositAccountScreen( onNavigateBack: () -> Unit, + navController: NavController, onFinish: () -> Unit, modifier: Modifier = Modifier, - viewModel: NewFixedDepositAccountViewmodel = viewModel(), + viewModel: CreateFixedDepositAccountViewmodel = koinViewModel(), ) { val state by viewModel.stateFlow.collectAsStateWithLifecycle() EventsEffect(viewModel.eventFlow) { event -> @@ -48,7 +55,8 @@ internal fun FixedDepositAccountScreen( } } FixedDepositAccountScaffold( - state = state, + navController = navController, + newFixedDepositAccountState = state, onAction = { viewModel.trySendAction(it) }, modifier = modifier, ) @@ -56,7 +64,8 @@ internal fun FixedDepositAccountScreen( @Composable private fun FixedDepositAccountScaffold( - state: NewFixedDepositAccountState, + navController: NavController, + newFixedDepositAccountState: NewFixedDepositAccountState, onAction: (NewFixedDepositAccountAction) -> Unit, modifier: Modifier = Modifier, ) { @@ -64,28 +73,29 @@ private fun FixedDepositAccountScaffold( listOf( Step(stringResource(Res.string.step_details)) { DetailsPage( - onNext = { onAction(NewFixedDepositAccountAction.NextStep) }, + 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) }, ) }, ) @@ -96,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 new file mode 100644 index 00000000000..81c39d9183f --- /dev/null +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/createFixedDepositAccountViewmodel.kt @@ -0,0 +1,252 @@ +/* + * 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 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.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( + savedStateHandle: SavedStateHandle, + private val fixedDepositRepository: FixedDepositRepository, +) : + BaseViewModel< + NewFixedDepositAccountState, + NewFixedDepositAccountEvent, + NewFixedDepositAccountAction, + >( + NewFixedDepositAccountState( + clientId = savedStateHandle.toRoute().clientId, + ), + ) { + + + init { + loadFixedDepositAccountTemplate() + } + + override fun handleAction(action: NewFixedDepositAccountAction) { + when (action) { + is NewFixedDepositAccountAction.OnNextPress -> 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() + 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( + 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( + fixedDepositAccountDetail = it.fixedDepositAccountDetail.copy( + showSubmissionDatePick = action.state, + ), + ) + } + } + + private fun handleOnProductNameChange(action: NewFixedDepositAccountAction.OnProductNameChange) { + mutableStateFlow.update { + it.copy( + fixedDepositAccountDetail = it.fixedDepositAccountDetail.copy( + productSelected = action.index, + ), + ) + } + loadFixedDepositAccountTemplate() + } + + private fun handleFieldOfficerChange(action: NewFixedDepositAccountAction.OnFieldOfficerChange) { + mutableStateFlow.update { + it.copy( + fixedDepositAccountDetail = it.fixedDepositAccountDetail.copy( + fieldOfficerIndex = action.index, + ), + ) + } + } + + private fun handleExternalIdChange(action: NewFixedDepositAccountAction.OnExternalIdChange) { + mutableStateFlow.update { + it.copy( + fixedDepositAccountDetail = it.fixedDepositAccountDetail.copy( + externalId = action.value, + ), + ) + } + } + + private fun handleOnDetailsSubmit() { + mutableStateFlow.update { + it.copy( + fixedDepositAccountDetail = it.fixedDepositAccountDetail.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 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 productSelected: Int = -1, + val submissionDate: String = DateHelper.getDateAsStringFromLong( + Clock.System.now().toEpochMilliseconds(), + ), + val fieldOfficerIndex: Int = -1, + val fieldOfficerError: String? = null, + val externalId: String = "", + val externalIdError: StringResource? = null, + val isMiniLoaderActive: Boolean = false, + val fieldOfficerOptions: List? = null, +) { + val isDetailsNextEnabled = submissionDate.isNotEmpty() && fieldOfficerIndex != -1 +} + +sealed class 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() + 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() + + 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 99017367eec..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 @@ -9,26 +9,168 @@ */ 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 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.fixedDepositAccountDetail.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(stringResource(Res.string.feature_client_charge_select)) } + }, + dismissButton = { + TextButton( + onClick = { + onAction(NewFixedDepositAccountAction.OnSubmissionDatePick(state = false)) + }, + ) { Text(stringResource(Res.string.feature_client_charge_cancel)) } + }, + ) { + DatePicker(state = submissionDatePickerState) + } + + } + 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.fixedDepositAccountDetail.submissionDate, + label = stringResource(Res.string.submission_on), + openDatePicker = { + onAction(NewFixedDepositAccountAction.OnSubmissionDatePick(true)) + }, + ) + Spacer(Modifier.height(DesignToken.padding.large)) + MifosTextFieldDropdown( + value = if (state.fixedDepositAccountDetail.fieldOfficerIndex == -1) { + "" + } else { + state.template.fieldOfficerOptions?.get(state.fixedDepositAccountDetail.fieldOfficerIndex)?.displayName + ?: "" + }, + label = stringResource(Res.string.Field_officer), + onValueChanged = {}, + onOptionSelected = { index, value -> + onAction(NewFixedDepositAccountAction.OnFieldOfficerChange(index)) + }, + options = state.template.fieldOfficerOptions?.map { + it.displayName ?: "" + } ?: emptyList(), + + ) + MifosOutlinedTextField( + value = state.fixedDepositAccountDetail.externalId, + onValueChange = { + onAction(NewFixedDepositAccountAction.OnExternalIdChange(it)) + }, + label = stringResource(Res.string.feature_client_external_id), + config = MifosTextFieldConfig( + isError = state.fixedDepositAccountDetail.externalIdError != null, + errorText = if (state.fixedDepositAccountDetail.externalIdError != null) stringResource(state.fixedDepositAccountDetail.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.OnNextPress) }, + isSecondButtonEnabled = state.fixedDepositAccountDetail.isDetailsNextEnabled, + modifier = Modifier.padding(top = DesignToken.padding.small), + ) } + }