diff --git a/core/data/src/main/java/com/mifos/core/data/di/DataModule.kt b/core/data/src/main/java/com/mifos/core/data/di/DataModule.kt index 6f40eab51f8..6c98fadb232 100644 --- a/core/data/src/main/java/com/mifos/core/data/di/DataModule.kt +++ b/core/data/src/main/java/com/mifos/core/data/di/DataModule.kt @@ -12,6 +12,7 @@ import com.mifos.core.data.repository.DocumentListRepository import com.mifos.core.data.repository.GroupDetailsRepository import com.mifos.core.data.repository.GroupListRepository import com.mifos.core.data.repository.GroupsListRepository +import com.mifos.core.data.repository.IndividualCollectionSheetDetailsRepository import com.mifos.core.data.repository.LoanAccountRepository import com.mifos.core.data.repository.NewIndividualCollectionSheetRepository import com.mifos.core.data.repository.PathTrackingRepository @@ -28,6 +29,7 @@ import com.mifos.core.data.repository_imp.DocumentListRepositoryImp import com.mifos.core.data.repository_imp.GroupDetailsRepositoryImp import com.mifos.core.data.repository_imp.GroupListRepositoryImp import com.mifos.core.data.repository_imp.GroupsListRepositoryImpl +import com.mifos.core.data.repository_imp.IndividualCollectionSheetDetailsRepositoryImp import com.mifos.core.data.repository_imp.LoanAccountRepositoryImp import com.mifos.core.data.repository_imp.NewIndividualCollectionSheetRepositoryImp import com.mifos.core.data.repository_imp.PathTrackingRepositoryImp @@ -89,6 +91,9 @@ abstract class DataModule { @Binds internal abstract fun bindDocumentListRepository(impl: DocumentListRepositoryImp): DocumentListRepository + @Binds + internal abstract fun bindIndividualCollectionSheetDetailsRepositoryImp(impl: IndividualCollectionSheetDetailsRepositoryImp): IndividualCollectionSheetDetailsRepository + @Binds internal abstract fun bindGroupListRepository(impl: GroupListRepositoryImp): GroupListRepository } \ No newline at end of file diff --git a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsRepository.kt b/core/data/src/main/java/com/mifos/core/data/repository/IndividualCollectionSheetDetailsRepository.kt similarity index 55% rename from mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsRepository.kt rename to core/data/src/main/java/com/mifos/core/data/repository/IndividualCollectionSheetDetailsRepository.kt index bbe8194016a..0c7fbdd73cc 100644 --- a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsRepository.kt +++ b/core/data/src/main/java/com/mifos/core/data/repository/IndividualCollectionSheetDetailsRepository.kt @@ -1,4 +1,4 @@ -package com.mifos.mifosxdroid.online.collectionsheetindividualdetails +package com.mifos.core.data.repository import com.mifos.core.network.GenericResponse import com.mifos.core.network.model.IndividualCollectionSheetPayload @@ -9,8 +9,8 @@ import rx.Observable */ interface IndividualCollectionSheetDetailsRepository { - fun saveIndividualCollectionSheet( - payload: IndividualCollectionSheetPayload? - ): Observable + suspend fun saveIndividualCollectionSheet( + payload: IndividualCollectionSheetPayload + ): GenericResponse } \ No newline at end of file diff --git a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsRepositoryImp.kt b/core/data/src/main/java/com/mifos/core/data/repository_imp/IndividualCollectionSheetDetailsRepositoryImp.kt similarity index 69% rename from mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsRepositoryImp.kt rename to core/data/src/main/java/com/mifos/core/data/repository_imp/IndividualCollectionSheetDetailsRepositoryImp.kt index 83f7c28c4b9..8c6ad15bcb3 100644 --- a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsRepositoryImp.kt +++ b/core/data/src/main/java/com/mifos/core/data/repository_imp/IndividualCollectionSheetDetailsRepositoryImp.kt @@ -1,5 +1,6 @@ -package com.mifos.mifosxdroid.online.collectionsheetindividualdetails +package com.mifos.core.data.repository_imp +import com.mifos.core.data.repository.IndividualCollectionSheetDetailsRepository import com.mifos.core.network.GenericResponse import com.mifos.core.network.datamanager.DataManagerCollectionSheet import com.mifos.core.network.model.IndividualCollectionSheetPayload @@ -12,7 +13,7 @@ import javax.inject.Inject class IndividualCollectionSheetDetailsRepositoryImp @Inject constructor(private val dataManagerCollection: DataManagerCollectionSheet) : IndividualCollectionSheetDetailsRepository { - override fun saveIndividualCollectionSheet(payload: IndividualCollectionSheetPayload?): Observable { + override suspend fun saveIndividualCollectionSheet(payload: IndividualCollectionSheetPayload): GenericResponse { return dataManagerCollection.saveIndividualCollectionSheet(payload) } } \ No newline at end of file diff --git a/core/designsystem/src/main/java/com/mifos/core/designsystem/icon/MifosIcon.kt b/core/designsystem/src/main/java/com/mifos/core/designsystem/icon/MifosIcon.kt index 5e25ba841b8..838ea202f1a 100644 --- a/core/designsystem/src/main/java/com/mifos/core/designsystem/icon/MifosIcon.kt +++ b/core/designsystem/src/main/java/com/mifos/core/designsystem/icon/MifosIcon.kt @@ -11,6 +11,7 @@ import androidx.compose.material.icons.rounded.ArrowBackIosNew import androidx.compose.material.icons.rounded.Check import androidx.compose.material.icons.rounded.Close import androidx.compose.material.icons.rounded.Delete +import androidx.compose.material.icons.rounded.Download import androidx.compose.material.icons.rounded.FilterList import androidx.compose.material.icons.rounded.KeyboardArrowDown import androidx.compose.material.icons.rounded.KeyboardArrowUp @@ -37,4 +38,5 @@ object MifosIcons { val moreVert = Icons.Rounded.MoreVert val fileTask = Icons.Default.AssignmentTurnedIn val cloudDownload = Icons.Default.CloudDownload + val save = Icons.Rounded.Download } \ No newline at end of file diff --git a/core/domain/src/main/java/com/mifos/core/domain/use_cases/SaveIndividualCollectionSheetUseCase.kt b/core/domain/src/main/java/com/mifos/core/domain/use_cases/SaveIndividualCollectionSheetUseCase.kt new file mode 100644 index 00000000000..3dea95eb6c8 --- /dev/null +++ b/core/domain/src/main/java/com/mifos/core/domain/use_cases/SaveIndividualCollectionSheetUseCase.kt @@ -0,0 +1,24 @@ +package com.mifos.core.domain.use_cases + +import com.mifos.core.common.utils.Resource +import com.mifos.core.data.repository.IndividualCollectionSheetDetailsRepository +import com.mifos.core.network.GenericResponse +import com.mifos.core.network.model.IndividualCollectionSheetPayload +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class SaveIndividualCollectionSheetUseCase @Inject constructor(private val repository: IndividualCollectionSheetDetailsRepository) { + + suspend operator fun invoke(payload: IndividualCollectionSheetPayload): Flow> = + flow { + try { + emit(Resource.Loading()) + val response = repository.saveIndividualCollectionSheet(payload) + emit(Resource.Success(response)) + } catch (exception: Exception) { + emit(Resource.Error(exception.message.toString())) + } + } + +} \ No newline at end of file diff --git a/core/network/src/main/java/com/mifos/core/network/datamanager/DataManagerCollectionSheet.kt b/core/network/src/main/java/com/mifos/core/network/datamanager/DataManagerCollectionSheet.kt index b19b5aea301..75fc51f56d1 100644 --- a/core/network/src/main/java/com/mifos/core/network/datamanager/DataManagerCollectionSheet.kt +++ b/core/network/src/main/java/com/mifos/core/network/datamanager/DataManagerCollectionSheet.kt @@ -31,9 +31,9 @@ class DataManagerCollectionSheet @Inject constructor( return mBaseApiManager.collectionSheetApi.getIndividualCollectionSheet(payload) } - fun saveIndividualCollectionSheet( + suspend fun saveIndividualCollectionSheet( payload: IndividualCollectionSheetPayload? - ): Observable { + ): GenericResponse { return mBaseApiManager.collectionSheetApi.saveindividualCollectionSheet(payload) } diff --git a/core/network/src/main/java/com/mifos/core/network/services/CollectionSheetService.kt b/core/network/src/main/java/com/mifos/core/network/services/CollectionSheetService.kt index 420732af65e..001bf4b6e90 100644 --- a/core/network/src/main/java/com/mifos/core/network/services/CollectionSheetService.kt +++ b/core/network/src/main/java/com/mifos/core/network/services/CollectionSheetService.kt @@ -28,9 +28,9 @@ interface CollectionSheetService { ): IndividualCollectionSheet @POST(APIEndPoint.COLLECTION_SHEET + "?command=saveCollectionSheet") - fun saveindividualCollectionSheet( + suspend fun saveindividualCollectionSheet( @Body payload: IndividualCollectionSheetPayload? - ): Observable + ): GenericResponse //Productive CollectionSheet Endpoints /** * Endpoint to fetch Productive CollectionSheet diff --git a/feature/collection-sheet/build.gradle.kts b/feature/collection-sheet/build.gradle.kts index 4bac1af28c7..c537283a102 100644 --- a/feature/collection-sheet/build.gradle.kts +++ b/feature/collection-sheet/build.gradle.kts @@ -12,6 +12,8 @@ dependencies { implementation(projects.core.domain) + implementation(libs.coil.kt.compose) + //DBFlow dependencies kapt(libs.dbflow.processor) implementation(libs.dbflow) diff --git a/feature/collection-sheet/src/main/java/com/mifos/feature/individual_collection_sheet/individual_collection_sheet_details/IndividualCollectionSheetDetailsScreen.kt b/feature/collection-sheet/src/main/java/com/mifos/feature/individual_collection_sheet/individual_collection_sheet_details/IndividualCollectionSheetDetailsScreen.kt new file mode 100644 index 00000000000..1c6738db1df --- /dev/null +++ b/feature/collection-sheet/src/main/java/com/mifos/feature/individual_collection_sheet/individual_collection_sheet_details/IndividualCollectionSheetDetailsScreen.kt @@ -0,0 +1,339 @@ +@file:OptIn(ExperimentalFoundationApi::class) + +package com.mifos.feature.individual_collection_sheet.individual_collection_sheet_details + +import android.widget.Toast +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.border +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.ChevronRight +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import coil.compose.AsyncImage +import com.mifos.core.designsystem.component.MifosCircularProgress +import com.mifos.core.designsystem.component.MifosScaffold +import com.mifos.core.designsystem.component.MifosSweetError +import com.mifos.core.designsystem.icon.MifosIcons +import com.mifos.core.designsystem.theme.Black +import com.mifos.core.designsystem.theme.DarkGray +import com.mifos.core.designsystem.theme.LightGray +import com.mifos.core.designsystem.theme.White +import com.mifos.core.model.BulkRepaymentTransactions +import com.mifos.core.network.model.IndividualCollectionSheetPayload +import com.mifos.core.objects.accounts.loan.PaymentTypeOptions +import com.mifos.core.objects.collectionsheet.ClientCollectionSheet +import com.mifos.core.objects.collectionsheet.IndividualCollectionSheet +import com.mifos.core.objects.collectionsheet.LoanAndClientName +import com.mifos.feature.collection_sheet.R + +@Composable +fun IndividualCollectionSheetDetailsScreen( + sheet: IndividualCollectionSheet, + onBackPressed: () -> Unit, + submit: (Int, IndividualCollectionSheetPayload, List, LoanAndClientName, List, Int) -> Unit +) { + + val viewModel: IndividualCollectionSheetDetailsViewModel = hiltViewModel() + val state by viewModel.individualCollectionSheetDetailsUiState.collectAsStateWithLifecycle() + val loansAndClientNames = viewModel.filterLoanAndClientNames(sheet.clients ?: emptyList()) + + IndividualCollectionSheetDetailsScreen( + sheet = sheet, + loansAndClientNames = loansAndClientNames, + state = state, + onBackPressed = onBackPressed, + onRetry = {}, + submit = submit, + onSave = { + viewModel.submitIndividualCollectionSheet(it) + } + ) +} + +@Composable +fun IndividualCollectionSheetDetailsScreen( + sheet: IndividualCollectionSheet, + loansAndClientNames: List, + state: IndividualCollectionSheetDetailsUiState, + onBackPressed: () -> Unit, + onRetry: () -> Unit, + submit: (Int, IndividualCollectionSheetPayload, List, LoanAndClientName, List, Int) -> Unit, + onSave: (IndividualCollectionSheetPayload) -> Unit +) { + val snackbarHostState by remember { mutableStateOf(SnackbarHostState()) } + var payload by rememberSaveable { mutableStateOf(IndividualCollectionSheetPayload()) } + var showLoading by rememberSaveable { mutableStateOf(false) } + var showError by rememberSaveable { mutableStateOf(false) } + + LaunchedEffect(key1 = loansAndClientNames) { + payload = IndividualCollectionSheetPayload() + for (loanAndClientName in loansAndClientNames) { + val loanCollectionSheet = loanAndClientName.loan + if (loanCollectionSheet != null) { + payload.bulkRepaymentTransactions.add( + BulkRepaymentTransactions( + loanCollectionSheet.loanId, + loanCollectionSheet.totalDue + + loanCollectionSheet.chargesDue + ) + ) + } + } + } + + when (state) { + is IndividualCollectionSheetDetailsUiState.Error -> { + showLoading = false + showError = true + } + + is IndividualCollectionSheetDetailsUiState.Loading -> showLoading = true + + is IndividualCollectionSheetDetailsUiState.SavedSuccessfully -> { + showLoading = false + Toast.makeText( + LocalContext.current, + stringResource(id = R.string.feature_collection_sheet_save_collection_sheet_success), + Toast.LENGTH_SHORT + ).show() + } + + IndividualCollectionSheetDetailsUiState.Empty -> Unit + } + + MifosScaffold( + icon = MifosIcons.arrowBack, + onBackPressed = onBackPressed, + title = stringResource(id = R.string.feature_collection_sheet_individual_collection_details), + actions = { + IconButton(onClick = { + onSave(payload) + }) { + Icon( + imageVector = MifosIcons.save, + contentDescription = null + ) + } + }, + snackbarHostState = snackbarHostState + ) { paddingValues -> + Column(modifier = Modifier.padding(paddingValues)) { + + if (showLoading) { + MifosCircularProgress() + } else if (showError) { + MifosSweetError( + message = stringResource( + id = R.string.feature_collection_sheet_failed_to_save_collection_sheet + ) + ) { + onRetry() + } + } else { + LazyColumn { + sheet.clients?.toList()?.let { + itemsIndexed(it) { index, client -> + IndividualCollectionSheetItem( + client = client, + index = index, + onClick = { + sheet.paymentTypeOptions?.let { paymentTypeOptions -> + submit( + index, + payload, + paymentTypeOptions.map { paymentTypeOption -> paymentTypeOption.name.toString() }, + loansAndClientNames[index], + paymentTypeOptions.toList(), + client.clientId + ) + } + } + ) + } + } + } + } + } + } +} + +@Composable +fun IndividualCollectionSheetItem( + client: ClientCollectionSheet, + index: Int, + onClick: () -> Unit +) { + OutlinedCard( + modifier = Modifier + .padding(6.dp) + .combinedClickable( + onClick = { + onClick() + }, + onLongClick = {} + ), + colors = CardDefaults.cardColors(White) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding( + start = 16.dp, + end = 16.dp, + top = 24.dp, + bottom = 24.dp + ), + verticalAlignment = Alignment.CenterVertically + ) { + AsyncImage( + modifier = Modifier + .size(40.dp) + .clip(CircleShape) + .border(width = 1.dp, LightGray, shape = CircleShape), + model = R.drawable.feature_collection_sheet_ic_dp_placeholder, + contentDescription = null + ) + Column( + modifier = Modifier + .weight(1f) + .padding(start = 16.dp) + ) { + client.clientName?.let { + Text( + text = it, + style = TextStyle( + fontSize = 16.sp, + fontWeight = FontWeight.Normal, + fontStyle = FontStyle.Normal, + color = Black + ) + ) + } + Row { + Text( + text = stringResource(id = R.string.feature_collection_sheet_total_due), + style = TextStyle( + fontSize = 14.sp, + fontWeight = FontWeight.Normal, + fontStyle = FontStyle.Normal, + color = Black + ) + ) + Spacer(modifier = Modifier.width(16.dp)) + Text( + text = client.loans?.get(index)?.totalDue.toString(), + style = TextStyle( + fontSize = 14.sp, + fontWeight = FontWeight.Normal, + fontStyle = FontStyle.Normal, + color = DarkGray + ) + ) + } + Row { + Text( + text = stringResource(id = R.string.feature_collection_sheet_total_charges), + style = TextStyle( + fontSize = 14.sp, + fontWeight = FontWeight.Normal, + fontStyle = FontStyle.Normal, + color = Black + ) + ) + Spacer(modifier = Modifier.width(16.dp)) + Text( + text = client.loans?.get(index)?.chargesDue.toString(), + style = TextStyle( + fontSize = 14.sp, + fontWeight = FontWeight.Normal, + fontStyle = FontStyle.Normal, + color = DarkGray + ) + ) + } + Text( + text = "${client.loans?.get(index)?.productShortName.toString()} (#${ + client.loans?.get( + index + )?.productShortName + })", + style = TextStyle( + fontSize = 14.sp, + fontWeight = FontWeight.Normal, + fontStyle = FontStyle.Normal, + color = Black + ) + ) + } + Icon( + imageVector = Icons.Rounded.ChevronRight, + contentDescription = null + ) + } + } +} + +class IndividualCollectionSheetDetailsUiStateProvider : + PreviewParameterProvider { + + override val values: Sequence + get() = sequenceOf( + IndividualCollectionSheetDetailsUiState.Error(R.string.feature_collection_sheet_failed_to_save_collection_sheet), + IndividualCollectionSheetDetailsUiState.Loading, + IndividualCollectionSheetDetailsUiState.SavedSuccessfully + ) + +} + +@Preview(showBackground = true) +@Composable +private fun IndividualCollectionSheetDetailsScreenPreview( + @PreviewParameter(IndividualCollectionSheetDetailsUiStateProvider::class) state: IndividualCollectionSheetDetailsUiState +) { + IndividualCollectionSheetDetailsScreen( + sheet = IndividualCollectionSheet(), + loansAndClientNames = emptyList(), + state = state, + onBackPressed = {}, + onRetry = {}, + submit = { _, _, _, _, _, _ -> }, + onSave = {} + ) +} \ No newline at end of file diff --git a/feature/collection-sheet/src/main/java/com/mifos/feature/individual_collection_sheet/individual_collection_sheet_details/IndividualCollectionSheetDetailsUiState.kt b/feature/collection-sheet/src/main/java/com/mifos/feature/individual_collection_sheet/individual_collection_sheet_details/IndividualCollectionSheetDetailsUiState.kt new file mode 100644 index 00000000000..474f8616b6d --- /dev/null +++ b/feature/collection-sheet/src/main/java/com/mifos/feature/individual_collection_sheet/individual_collection_sheet_details/IndividualCollectionSheetDetailsUiState.kt @@ -0,0 +1,15 @@ +package com.mifos.feature.individual_collection_sheet.individual_collection_sheet_details + +/** + * Created by Aditya Gupta on 10/08/23. + */ +sealed class IndividualCollectionSheetDetailsUiState { + + data object Empty : IndividualCollectionSheetDetailsUiState() + + data object Loading : IndividualCollectionSheetDetailsUiState() + + data class Error(val message: Int) : IndividualCollectionSheetDetailsUiState() + + data object SavedSuccessfully : IndividualCollectionSheetDetailsUiState() +} \ No newline at end of file diff --git a/feature/collection-sheet/src/main/java/com/mifos/feature/individual_collection_sheet/individual_collection_sheet_details/IndividualCollectionSheetDetailsViewModel.kt b/feature/collection-sheet/src/main/java/com/mifos/feature/individual_collection_sheet/individual_collection_sheet_details/IndividualCollectionSheetDetailsViewModel.kt new file mode 100644 index 00000000000..bd6311c96a9 --- /dev/null +++ b/feature/collection-sheet/src/main/java/com/mifos/feature/individual_collection_sheet/individual_collection_sheet_details/IndividualCollectionSheetDetailsViewModel.kt @@ -0,0 +1,65 @@ +package com.mifos.feature.individual_collection_sheet.individual_collection_sheet_details + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.mifos.core.common.utils.Resource +import com.mifos.core.domain.use_cases.SaveIndividualCollectionSheetUseCase +import com.mifos.core.network.model.IndividualCollectionSheetPayload +import com.mifos.core.objects.collectionsheet.ClientCollectionSheet +import com.mifos.core.objects.collectionsheet.LoanAndClientName +import com.mifos.feature.collection_sheet.R +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import rx.Observable +import javax.inject.Inject + +@HiltViewModel +class IndividualCollectionSheetDetailsViewModel @Inject constructor( + private val saveIndividualCollectionSheetUseCase: SaveIndividualCollectionSheetUseCase +) : ViewModel() { + + private val _individualCollectionSheetDetailsUiState = + MutableStateFlow( + IndividualCollectionSheetDetailsUiState.Empty + ) + val individualCollectionSheetDetailsUiState = + _individualCollectionSheetDetailsUiState.asStateFlow() + + fun submitIndividualCollectionSheet(payload: IndividualCollectionSheetPayload) = + viewModelScope.launch(Dispatchers.IO) { + saveIndividualCollectionSheetUseCase(payload).collect { result -> + when (result) { + is Resource.Error -> _individualCollectionSheetDetailsUiState.value = + IndividualCollectionSheetDetailsUiState.Error(R.string.feature_collection_sheet_failed_to_save_collection_sheet) + + is Resource.Loading -> _individualCollectionSheetDetailsUiState.value = + IndividualCollectionSheetDetailsUiState.Loading + + is Resource.Success -> _individualCollectionSheetDetailsUiState.value = + IndividualCollectionSheetDetailsUiState.SavedSuccessfully + } + } + } + + fun filterLoanAndClientNames(clientCollectionSheets: List): List { + val loansAndClientNames: MutableList = ArrayList() + Observable.from(clientCollectionSheets) + .subscribe { clientCollectionSheet -> + clientCollectionSheet.loans?.let { + for (loanCollectionSheet in it) { + loansAndClientNames.add( + LoanAndClientName( + loanCollectionSheet, + clientCollectionSheet.clientName, + clientCollectionSheet.clientId + ) + ) + } + } + } + return loansAndClientNames + } +} \ No newline at end of file diff --git a/feature/collection-sheet/src/main/res/drawable/feature_collection_sheet_ic_dp_placeholder.png b/feature/collection-sheet/src/main/res/drawable/feature_collection_sheet_ic_dp_placeholder.png new file mode 100644 index 00000000000..4f7005f778b Binary files /dev/null and b/feature/collection-sheet/src/main/res/drawable/feature_collection_sheet_ic_dp_placeholder.png differ diff --git a/feature/collection-sheet/src/main/res/values/strings.xml b/feature/collection-sheet/src/main/res/values/strings.xml index 40bf42371be..d88c9a1fc3c 100644 --- a/feature/collection-sheet/src/main/res/values/strings.xml +++ b/feature/collection-sheet/src/main/res/values/strings.xml @@ -19,4 +19,10 @@ Member: Fill Now + Failed to Save Collection Sheet + Collection Sheet Saved Successfully + Total Charges + Total due + Collection Details + \ No newline at end of file diff --git a/mifosng-android/src/main/java/com/mifos/mifosxdroid/injection/module/RepositoryModule.kt b/mifosng-android/src/main/java/com/mifos/mifosxdroid/injection/module/RepositoryModule.kt index 79fc0813c81..6572ce7dec6 100644 --- a/mifosng-android/src/main/java/com/mifos/mifosxdroid/injection/module/RepositoryModule.kt +++ b/mifosng-android/src/main/java/com/mifos/mifosxdroid/injection/module/RepositoryModule.kt @@ -64,8 +64,6 @@ import com.mifos.mifosxdroid.online.clientlist.ClientListRepository import com.mifos.mifosxdroid.online.clientlist.ClientListRepositoryImp import com.mifos.mifosxdroid.online.collectionsheet.CollectionSheetRepository import com.mifos.mifosxdroid.online.collectionsheet.CollectionSheetRepositoryImp -import com.mifos.mifosxdroid.online.collectionsheetindividualdetails.IndividualCollectionSheetDetailsRepository -import com.mifos.mifosxdroid.online.collectionsheetindividualdetails.IndividualCollectionSheetDetailsRepositoryImp import com.mifos.mifosxdroid.online.createnewclient.CreateNewClientRepository import com.mifos.mifosxdroid.online.createnewclient.CreateNewClientRepositoryImp import com.mifos.mifosxdroid.online.createnewgroup.CreateNewGroupRepository @@ -243,11 +241,6 @@ class RepositoryModule { return CollectionSheetRepositoryImp(dataManager) } - @Provides - fun providesIndividualCollectionSheetDetailsRepository(dataManagerCollection: DataManagerCollectionSheet): IndividualCollectionSheetDetailsRepository { - return IndividualCollectionSheetDetailsRepositoryImp(dataManagerCollection) - } - @Provides fun providesCreateNewClientRepository( dataManagerClient: DataManagerClient, diff --git a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsFragment.kt b/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsFragment.kt index a6b7ae6e53f..608bb996d7a 100644 --- a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsFragment.kt +++ b/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsFragment.kt @@ -2,189 +2,81 @@ package com.mifos.mifosxdroid.online.collectionsheetindividualdetails import android.os.Bundle import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.lifecycle.ViewModelProvider +import androidx.compose.ui.platform.ComposeView import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import androidx.recyclerview.widget.LinearLayoutManager -import com.mifos.core.common.utils.Constants -import com.mifos.core.model.BulkRepaymentTransactions import com.mifos.core.network.model.IndividualCollectionSheetPayload +import com.mifos.core.objects.accounts.loan.PaymentTypeOptions import com.mifos.core.objects.collectionsheet.IndividualCollectionSheet import com.mifos.core.objects.collectionsheet.LoanAndClientName -import com.mifos.mifosxdroid.R -import com.mifos.mifosxdroid.adapters.IndividualCollectionSheetDetailsAdapter -import com.mifos.mifosxdroid.adapters.IndividualCollectionSheetDetailsAdapter.ListAdapterListener +import com.mifos.feature.individual_collection_sheet.individual_collection_sheet_details.IndividualCollectionSheetDetailsScreen import com.mifos.mifosxdroid.core.MifosBaseFragment -import com.mifos.mifosxdroid.core.util.Toaster -import com.mifos.mifosxdroid.databinding.IndividualCollectionsSheetDetailsBinding -import com.mifos.mifosxdroid.online.GenerateCollectionSheetActivity import dagger.hilt.android.AndroidEntryPoint /** * Created by aksh on 20/6/18. */ @AndroidEntryPoint -class IndividualCollectionSheetDetailsFragment : MifosBaseFragment(), OnRetrieveSheetItemData, - ListAdapterListener { +class IndividualCollectionSheetDetailsFragment : MifosBaseFragment() { - private lateinit var binding: IndividualCollectionsSheetDetailsBinding private val arg: IndividualCollectionSheetDetailsFragmentArgs by navArgs() - private lateinit var viewModel: IndividualCollectionSheetDetailsViewModel + private lateinit var sheet: IndividualCollectionSheet + private lateinit var actualDisbursementDate: String + private lateinit var transactionDate: String - var sheetsAdapter: IndividualCollectionSheetDetailsAdapter? = null - private var sheet: IndividualCollectionSheet? = null - private var paymentTypeList: List? = null - private var loansAndClientNames: List = emptyList() - var payload: IndividualCollectionSheetPayload? = null - private val requestCode = 1 - private var actualDisbursementDate: String? = null - private var transactionDate: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if (savedInstanceState != null) { - sheet = - savedInstanceState[Constants.EXTRA_COLLECTION_INDIVIDUAL] as IndividualCollectionSheet - showCollectionSheetViews(sheet) - } sheet = arg.sheet actualDisbursementDate = arg.actualDisbursementDate transactionDate = arg.transactionDate - setHasOptionsMenu(true) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = IndividualCollectionsSheetDetailsBinding.inflate(inflater, container, false) - setToolbarTitle(getStringMessage(R.string.individual_collection_sheet)) - sheetsAdapter = IndividualCollectionSheetDetailsAdapter(requireContext(), this) - viewModel = ViewModelProvider(this)[IndividualCollectionSheetDetailsViewModel::class.java] - payload = (activity as GenerateCollectionSheetActivity).payload - showCollectionSheetViews(sheet) - - viewModel.individualCollectionSheetDetailsUiState.observe(viewLifecycleOwner) { - when (it) { - is IndividualCollectionSheetDetailsUiState.ShowError -> { - showProgressbar(false) - showError(it.message) - } - - is IndividualCollectionSheetDetailsUiState.ShowProgressbar -> showProgressbar(true) - is IndividualCollectionSheetDetailsUiState.ShowSuccess -> { - showProgressbar(false) - showSuccess() - } - } - } - - return binding.root - } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.menu_individual_collectionsheet, menu) - super.onCreateOptionsMenu(menu, inflater) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.item_submit_sheet -> { - submitSheet() - return true - } - } - return super.onOptionsItemSelected(item) - } - - private fun showCollectionSheetViews(sheet: IndividualCollectionSheet?) { - paymentTypeList = viewModel.filterPaymentTypeOptions(sheet?.paymentTypeOptions) - loansAndClientNames = viewModel.filterLoanAndClientNames(sheet?.clients) - - //Initialize payload's BulkRepaymentTransactions array with default values. - //The changes made (if any) will be updated by the interface 'OnRetrieveSheetItemData' - - //methods. - if (payload == null) { - payload = IndividualCollectionSheetPayload() - for (loanAndClientName in viewModel.filterLoanAndClientNames(sheet?.clients)) { - val loanCollectionSheet = loanAndClientName.loan - if (loanCollectionSheet != null) { - payload?.bulkRepaymentTransactions?.add( - BulkRepaymentTransactions( - loanCollectionSheet.loanId, - loanCollectionSheet.totalDue + - loanCollectionSheet.chargesDue + return ComposeView(requireContext()).apply { + setContent { + IndividualCollectionSheetDetailsScreen( + sheet = sheet, + onBackPressed = { + findNavController().popBackStack() + }, + submit = { position, payload, paymentTypeOptionsName, loansAndClientName, paymentTypeOptions, clientId -> + showPayment( + position, + payload, + paymentTypeOptionsName, + loansAndClientName, + paymentTypeOptions, + clientId ) - ) - } + } + ) } } - val layoutManager = LinearLayoutManager(context) - binding.recyclerCollections.layoutManager = layoutManager - binding.recyclerCollections.adapter = sheetsAdapter - sheetsAdapter?.setSheetItemClickListener(this) - sheetsAdapter?.setLoans(loansAndClientNames) - sheetsAdapter?.setPaymentTypeList(paymentTypeList) - sheetsAdapter?.setPaymentTypeOptionsList(sheet?.paymentTypeOptions) - sheetsAdapter?.notifyDataSetChanged() - } - - private fun showSuccess() { - Toaster.show(binding.root, getStringMessage(R.string.collectionsheet_submit_success)) - } - - private fun showError(error: String?) { - Toaster.show(binding.root, error) - } - - private fun showProgressbar(b: Boolean) { - if (b) { - showMifosProgressDialog() - } else { - hideMifosProgressDialog() - } } - override fun onShowSheetMandatoryItem(transaction: BulkRepaymentTransactions, position: Int) {} - override fun onSaveAdditionalItem(transaction: BulkRepaymentTransactions, position: Int) { - payload!!.bulkRepaymentTransactions[position] = transaction - } - - override fun listItemPosition(position: Int) { - val paymentTypeOptionList: Array? = paymentTypeList?.toTypedArray() - val paymentTypeOptions = sheet?.paymentTypeOptions?.toTypedArray() - val current = loansAndClientNames[position] - val clientId = current.id - val action = payload?.let { - paymentTypeOptionList?.let { it1 -> - paymentTypeOptions?.let { it2 -> - IndividualCollectionSheetDetailsFragmentDirections.actionIndividualCollectionSheetDetailsFragmentToPaymentDetailsFragment( - position, - it, - it1, - current, - it2, - clientId - ) - } - } - } - action?.let { findNavController().navigate(it) } - } - - private fun submitSheet() { - if (payload == null) { - Toaster.show(binding.root, getStringMessage(R.string.error_generate_sheet_first)) - } else { - payload?.actualDisbursementDate = actualDisbursementDate - payload?.transactionDate = transactionDate - viewModel.submitIndividualCollectionSheet(payload) - } + private fun showPayment( + position: Int, + payload: IndividualCollectionSheetPayload, + paymentTypeOptionsName: List, + loansAndClientName: LoanAndClientName, + paymentTypeOptions: List, + clientId: Int + ) { + val action = + IndividualCollectionSheetDetailsFragmentDirections.actionIndividualCollectionSheetDetailsFragmentToPaymentDetailsFragment( + position, + payload, + paymentTypeOptionsName.toTypedArray(), + loansAndClientName, + paymentTypeOptions.toTypedArray(), + clientId + ) + findNavController().navigate(action) } } \ No newline at end of file diff --git a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsUiState.kt b/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsUiState.kt deleted file mode 100644 index f36f5836d1a..00000000000 --- a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsUiState.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.mifos.mifosxdroid.online.collectionsheetindividualdetails - -/** - * Created by Aditya Gupta on 10/08/23. - */ -sealed class IndividualCollectionSheetDetailsUiState { - - data object ShowProgressbar : IndividualCollectionSheetDetailsUiState() - - data class ShowError(val message: String) : IndividualCollectionSheetDetailsUiState() - - data object ShowSuccess : IndividualCollectionSheetDetailsUiState() -} \ No newline at end of file diff --git a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsViewModel.kt b/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsViewModel.kt deleted file mode 100644 index 033dd498cc4..00000000000 --- a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/collectionsheetindividualdetails/IndividualCollectionSheetDetailsViewModel.kt +++ /dev/null @@ -1,90 +0,0 @@ -package com.mifos.mifosxdroid.online.collectionsheetindividualdetails - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import com.mifos.core.network.GenericResponse -import com.mifos.core.network.model.IndividualCollectionSheetPayload -import com.mifos.core.objects.accounts.loan.PaymentTypeOptions -import com.mifos.core.objects.collectionsheet.ClientCollectionSheet -import com.mifos.core.objects.collectionsheet.LoanAndClientName -import dagger.hilt.android.lifecycle.HiltViewModel -import retrofit2.HttpException -import rx.Observable -import rx.Subscriber -import rx.android.schedulers.AndroidSchedulers -import rx.plugins.RxJavaPlugins -import rx.schedulers.Schedulers -import javax.inject.Inject - -/** - * Created by Aditya Gupta on 10/08/23. - */ -@HiltViewModel -class IndividualCollectionSheetDetailsViewModel @Inject constructor(private val repository: IndividualCollectionSheetDetailsRepositoryImp) : - ViewModel() { - - private val _individualCollectionSheetDetailsUiState = - MutableLiveData() - - val individualCollectionSheetDetailsUiState: LiveData - get() = _individualCollectionSheetDetailsUiState - - fun submitIndividualCollectionSheet(individualCollectionSheetPayload: IndividualCollectionSheetPayload?) { - _individualCollectionSheetDetailsUiState.value = - IndividualCollectionSheetDetailsUiState.ShowProgressbar - repository - .saveIndividualCollectionSheet(individualCollectionSheetPayload) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeOn(Schedulers.io()) - .subscribe(object : Subscriber() { - override fun onCompleted() {} - override fun onError(e: Throwable) { - try { - if (e is HttpException) { - val errorMessage = e.response()?.errorBody() - ?.string() - _individualCollectionSheetDetailsUiState.value = errorMessage?.let { - IndividualCollectionSheetDetailsUiState.ShowError( - it - ) - } - } - } catch (throwable: Throwable) { - RxJavaPlugins.getInstance().errorHandler.handleError(e) - } - } - - override fun onNext(genericResponse: GenericResponse?) { - _individualCollectionSheetDetailsUiState.value = - IndividualCollectionSheetDetailsUiState.ShowSuccess - } - }) - } - - fun filterPaymentTypeOptions(paymentTypeOptionsList: List?): List { - val paymentList: MutableList = ArrayList() - Observable.from(paymentTypeOptionsList) - .subscribe { paymentTypeOption -> paymentTypeOption.name?.let { paymentList.add(it) } } - return paymentList - } - - fun filterLoanAndClientNames(clientCollectionSheets: List?): List { - val loansAndClientNames: MutableList = ArrayList() - Observable.from(clientCollectionSheets) - .subscribe { clientCollectionSheet -> - if (clientCollectionSheet.loans != null) { - for (loanCollectionSheet in clientCollectionSheet.loans!!) { - loansAndClientNames.add( - LoanAndClientName( - loanCollectionSheet, - clientCollectionSheet.clientName, - clientCollectionSheet.clientId - ) - ) - } - } - } - return loansAndClientNames - } -} \ No newline at end of file