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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import com.mifos.core.data.repository.LoanAccountSummaryRepository
import com.mifos.core.data.repository.LoanChargeFormRepository
import com.mifos.core.data.repository.LoanChargeOffRepository
import com.mifos.core.data.repository.LoanChargeRepository
import com.mifos.core.data.repository.LoanCreateGuarantorRepository
import com.mifos.core.data.repository.LoanRepaymentRepository
import com.mifos.core.data.repository.LoanRepaymentScheduleRepository
import com.mifos.core.data.repository.LoanReschedulesRepository
Expand Down Expand Up @@ -117,6 +118,7 @@ import com.mifos.core.data.repositoryImp.LoanAccountSummaryRepositoryImp
import com.mifos.core.data.repositoryImp.LoanChargeFormRepositoryImp
import com.mifos.core.data.repositoryImp.LoanChargeOffRepositoryImpl
import com.mifos.core.data.repositoryImp.LoanChargeRepositoryImp
import com.mifos.core.data.repositoryImp.LoanCreateGuarantorRepositoryImp
import com.mifos.core.data.repositoryImp.LoanRepaymentRepositoryImp
import com.mifos.core.data.repositoryImp.LoanRepaymentScheduleRepositoryImp
import com.mifos.core.data.repositoryImp.LoanReschedulesRepositoryImpl
Expand Down Expand Up @@ -200,6 +202,7 @@ val RepositoryModule = module {
singleOf(::LoanTransactionsRepositoryImp) bind LoanTransactionsRepository::class
singleOf(::LoanReschedulesRepositoryImpl) bind LoanReschedulesRepository::class
singleOf(::LoanChargeOffRepositoryImpl) bind LoanChargeOffRepository::class
singleOf(::LoanCreateGuarantorRepositoryImp) bind LoanCreateGuarantorRepository::class

// Account Transfer
singleOf(::AmountTransferRepositoryImp) bind AmountTransferRepository::class
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2026 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/mifos-x-field-officer-app/blob/master/LICENSE.md
*/
package com.mifos.core.data.mappers.loan

import com.mifos.core.model.objects.account.loan.guarantor.CreateGuarantor
import com.mifos.core.model.objects.account.loan.guarantor.CreateGuarantorInput
import com.mifos.core.model.objects.account.loan.guarantor.GuarantorAccountTemplate
import com.mifos.core.model.objects.account.loan.guarantor.GuarantorRelationshipOption
import com.mifos.core.model.objects.account.loan.guarantor.GuarantorTemplate
import com.mifos.core.model.objects.account.loan.guarantor.GuarantorType
import com.mifos.core.network.dto.loans.CreateGuarantorResponseDto
import com.mifos.core.network.dto.loans.GuarantorAccountTemplateDto
import com.mifos.core.network.dto.loans.GuarantorRelationshipOptionDto
import com.mifos.core.network.dto.loans.GuarantorRequestDto
import com.mifos.core.network.dto.loans.GuarantorTemplateDto
import com.mifos.core.network.dto.loans.GuarantorTypeDto

fun GuarantorTemplateDto.toDomain(): GuarantorTemplate =
GuarantorTemplate(
allowedClientRelationshipTypes = allowedClientRelationshipTypes.map { it.toDomain() },
guarantorTypeOptions = guarantorTypeOptions.map { it.toDomain() },
)

fun GuarantorRelationshipOptionDto.toDomain(): GuarantorRelationshipOption =
GuarantorRelationshipOption(
id = id,
name = name,
)

fun CreateGuarantorResponseDto.toDomain(): CreateGuarantor =
CreateGuarantor(
resourceId = resourceId,
loanId = loanId,
officeId = officeId,
)

fun CreateGuarantorInput.toDto(): GuarantorRequestDto {
return GuarantorRequestDto(
clientRelationshipTypeId = clientRelationshipTypeId,
dateFormat = dateFormat,
entityId = entityId,
guarantorTypeId = guarantorTypeId,
locale = locale,
firstname = firstname,
lastname = lastname,
dob = dob,
addressLine1 = addressLine1,
addressLine2 = addressLine2,
city = city,
zip = zip,
mobileNumber = mobileNumber,
housePhoneNumber = housePhoneNumber,
)
}

fun GuarantorAccountTemplateDto.toDomain(): GuarantorAccountTemplate {
return GuarantorAccountTemplate(
guarantorType = guarantorType.toDomain(),
status = status,
externalGuarantor = externalGuarantor,
existingGroup = existingGroup,
existingClient = existingClient,
staffMember = staffMember,
)
}

fun GuarantorTypeDto.toDomain(): GuarantorType {
return GuarantorType(
id = id,
code = code,
value = value,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2026 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/mifos-x-field-officer-app/blob/master/LICENSE.md
*/
package com.mifos.core.data.repository

import com.mifos.core.common.utils.DataState
import com.mifos.core.model.objects.account.loan.guarantor.CreateGuarantor
import com.mifos.core.model.objects.account.loan.guarantor.CreateGuarantorInput
import com.mifos.core.model.objects.account.loan.guarantor.GuarantorAccountTemplate
import com.mifos.core.model.objects.account.loan.guarantor.GuarantorTemplate

interface LoanCreateGuarantorRepository {

suspend fun getGuarantorTemplate(loanId: Int): DataState<GuarantorTemplate>

suspend fun createGuarantor(
loanId: Int,
createGuarantorInput: CreateGuarantorInput,
): DataState<CreateGuarantor>

suspend fun getGuarantorAccountTemplate(
loanId: Int,
clientId: Int,
): DataState<GuarantorAccountTemplate>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2026 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/mifos-x-field-officer-app/blob/master/LICENSE.md
*/
package com.mifos.core.data.repositoryImp

import com.mifos.core.common.utils.DataState
import com.mifos.core.data.mappers.loan.toDomain
import com.mifos.core.data.mappers.loan.toDto
import com.mifos.core.data.repository.LoanCreateGuarantorRepository
import com.mifos.core.data.util.NetworkMonitor
import com.mifos.core.data.util.runAsDataState
import com.mifos.core.model.objects.account.loan.guarantor.CreateGuarantor
import com.mifos.core.model.objects.account.loan.guarantor.CreateGuarantorInput
import com.mifos.core.model.objects.account.loan.guarantor.GuarantorAccountTemplate
import com.mifos.core.model.objects.account.loan.guarantor.GuarantorTemplate
import com.mifos.core.network.datamanager.DataManagerLoan
import kotlinx.coroutines.CoroutineDispatcher

class LoanCreateGuarantorRepositoryImp(
private val dataManagerLoan: DataManagerLoan,
private val ioDispatcher: CoroutineDispatcher,
private val networkMonitor: NetworkMonitor,
) : LoanCreateGuarantorRepository {

override suspend fun getGuarantorTemplate(loanId: Int): DataState<GuarantorTemplate> {
return runAsDataState(
networkMonitor,
ioDispatcher,
) {
dataManagerLoan.getGuarantorTemplate(loanId).toDomain()
}
}

override suspend fun createGuarantor(
loanId: Int,
createGuarantorInput: CreateGuarantorInput,
): DataState<CreateGuarantor> {
return runAsDataState(
networkMonitor,
ioDispatcher,
) {
dataManagerLoan.createGuarantor(loanId, createGuarantorInput.toDto()).toDomain()
}
}

override suspend fun getGuarantorAccountTemplate(
loanId: Int,
clientId: Int,
): DataState<GuarantorAccountTemplate> {
return runAsDataState(
networkMonitor,
ioDispatcher,
) {
dataManagerLoan
.getGuarantorAccountTemplate(
loanId = loanId,
clientId = clientId,
)
.toDomain()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright 2026 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/mifos-x-field-officer-app/blob/master/LICENSE.md
*/
package com.mifos.core.designsystem.component

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuAnchorType
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.input.ImeAction
import com.mifos.core.designsystem.theme.DesignToken
import com.mifos.core.designsystem.theme.MifosTypography
import template.core.base.designsystem.theme.KptTheme

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MifosSearchableDropdown(
value: String,
onValueChanged: (String) -> Unit,
onOptionSelected: (Int, String) -> Unit,
options: List<String>,
modifier: Modifier = Modifier
.clip(DesignToken.shapes.medium)
.fillMaxWidth(),
label: String? = null,
enabled: Boolean = true,
errorMessage: String? = null,
) {
var expanded by remember { mutableStateOf(false) }

ExposedDropdownMenuBox(
expanded = expanded && enabled && options.isNotEmpty(),
onExpandedChange = {
if (enabled) {
expanded = !expanded
}
},
) {
OutlinedTextField(
value = value,
onValueChange = {
onValueChanged(it)
expanded = true
},
enabled = enabled,
readOnly = false,
isError = errorMessage != null,
supportingText = {
errorMessage?.let {
Text(
text = it,
color = KptTheme.colorScheme.error,
)
}
},
label = {
label?.let {
Text(text = it)
}
},
modifier = modifier.menuAnchor(
type = ExposedDropdownMenuAnchorType.PrimaryEditable,
enabled = enabled,
),
shape = DesignToken.shapes.medium,
colors = OutlinedTextFieldDefaults.colors(
focusedBorderColor = KptTheme.colorScheme.secondaryContainer,
unfocusedBorderColor = KptTheme.colorScheme.secondaryContainer,
),
maxLines = 1,
textStyle = MifosTypography.bodyLarge,
keyboardOptions = KeyboardOptions.Default.copy(
imeAction = ImeAction.Done,
),
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
},
)

ExposedDropdownMenu(
expanded = expanded && options.isNotEmpty(),
onDismissRequest = {
expanded = false
},
) {
options.forEachIndexed { index, item ->
DropdownMenuItem(
text = {
Text(text = item)
},
onClick = {
expanded = false
onValueChanged(item)
onOptionSelected(index, item)
},
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ import com.mifos.core.domain.useCases.ValidateServerEndPointUseCase
import com.mifos.core.domain.useCases.ValidateServerPortUseCase
import com.mifos.core.domain.useCases.ValidateServerProtocolUseCase
import com.mifos.core.domain.useCases.ValidateServerTenantUseCase
import com.mifos.core.domain.useCases.createGuarantor.CreateGuarantorUseCase
import com.mifos.core.domain.useCases.createGuarantor.GetGuarantorAccountTemplateUseCase
import com.mifos.core.domain.useCases.createGuarantor.GetGuarantorTemplateUseCase
Comment thread
sahilshivekar marked this conversation as resolved.
import com.mifos.core.domain.useCases.loanChargeOff.GetLoanChargeOffTemplateUseCase
import com.mifos.core.domain.useCases.loanChargeOff.LoanChargeOffUseCase
import org.koin.core.module.dsl.factoryOf
Expand All @@ -104,6 +107,8 @@ val UseCaseModule = module {
factoryOf(::ApproveCheckerUseCase)
factoryOf(::ApproveSavingsApplicationUseCase)
factoryOf(::CreateChargesUseCase)
factoryOf(::CreateGuarantorUseCase)
factoryOf(::GetGuarantorAccountTemplateUseCase)
factoryOf(::CreateClientIdentifierUseCase)
factoryOf(::CalculateLoanScheduleUseCase)
factoryOf(::CreateGroupLoansAccountUseCase)
Expand Down Expand Up @@ -136,6 +141,7 @@ val UseCaseModule = module {
factoryOf(::GetDocumentsListUseCase)
factoryOf(::GetGroupLoansAccountTemplateUseCase)
factoryOf(::GetGroupSavingsAccountTemplateByProductUseCase)
factoryOf(::GetGuarantorTemplateUseCase)
factoryOf(::GetGroupsByCenterUseCase)
factoryOf(::GetGroupsByOfficeUseCase)
factoryOf(::GetIndividualCollectionSheetUseCase)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2026 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/mifos-x-field-officer-app/blob/master/LICENSE.md
*/
package com.mifos.core.domain.useCases.createGuarantor

import com.mifos.core.common.utils.DataState
import com.mifos.core.data.repository.LoanCreateGuarantorRepository
import com.mifos.core.model.objects.account.loan.guarantor.CreateGuarantor
import com.mifos.core.model.objects.account.loan.guarantor.CreateGuarantorInput

class CreateGuarantorUseCase(
private val repository: LoanCreateGuarantorRepository,
) {
suspend operator fun invoke(
loanId: Int,
createGuarantorInput: CreateGuarantorInput,
): DataState<CreateGuarantor> = repository.createGuarantor(loanId, createGuarantorInput)
}
Loading
Loading